DEV Community

Bruno Oliveira
Bruno Oliveira

Posted on • Updated on

Flask series part 9: Adding a navbar by using template inheritance

Introduction

After improving the user experience of our application, we can capitalize on that and add a navbar to our Flask application.
A navbar is a component that's used to navigate between views in an application and can also be used to allow a user to login into an application and see a personal dashboard for example. It will make our application look and feel more usable and professional and it'll be the gateway for additional functionalities.

Idea behind the component

The navigation bar will be available at all times, on every view after a user logs in, so its dashboard and login information are always visible.
Additionally, some views will only be available to logged in users, such as a user's personal information dashboard and the possibility of adding new recipes to the application.
Pheraphs more interesting for us, from a more technical point of view, is the fact that the navbar will be the same, no matter the view which we are in. In other words, the navbar is naturally a shared component. It will look like this:

topnav

As we can see, it contains some navigation buttons and a login menu as well. Let's see how Flask together with Jinja will allow us to restructure our front end code to build such a component.

Flask template inheritance

When designing a shared component, we have some alternative design approaches we can take:

  • Create multiple "views" with a shared data source

  • Create multiple "views", each coupled to its individual data source, which would force us to have a way to share and sync the state between each individual instance of the component

  • Reuse both the view and data sources and simply restructure the application to allow this

Some of these approaches can be better suited for a particular development strategy.

Separate teams might benefit from having full control over the component, on which case, approach two would be the best.

Using a single data source and shared views can be useful for example, if you're developing applications, both for mobile environments and desktop environments. On this scenario, the data source of the component can be shared between different target devices, since we want the component to use, provide or consume the same information, the trick is, we want to show it in different ways, depending on the type of device, so the view part of the component can be adaptive.

The last scenario is where Flask template inheritance will enter.

It allows us to write a single HTML file to represent our component, and, then, all other template files can inherit this one, effectively reusing the component instance. Then we have a single component whose HTML is then injected into each of our views, while the component is driven from a single endpoint regarding data as well.

In order to do this, we first need to restructure our current HTML templates setup so that it supports inheritance.

Template inheritance is a mechanism used by Jinja to enable templates to be reused, by ensuring that common elements are placed in a common template, from which all other templates can inherit.

Template inheritance allows us to build a base "skeleton" template that contains all the common elements of your site and defines blocks that child templates can override.

Sounds complicated but is very basic. It's easiest to understand it by starting with an example.

Let's start with a base template:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <link rel="stylesheet" href="style.css" />
  <title>{% block title %}{% endblock %} - My Webpage</title>
  {% block html_head %}{% endblock %}
</head>
<body>
  <div id="content">
    {% block content %}{% endblock %}
  </div>

  <div id="footer">
    {% block footer %}
    &copy; Copyright 2006 by <a href="http://mydomain.tld">myself</a>.
    {% endblock %}
  </div>
</body>

This template, which we'll call base.html, defines a simple HTML skeleton document that you might use for a simple two-column page. It's the job of "child" templates to fill the empty blocks with content.

In this example, the {% block %} tags define four blocks that child templates can fill in. All the block tag does is to tell the template engine that a child template may override those portions of the template.

Let's see now, how to create a possible child template for the base template defined above:

{% extends "base.html" %}
{% block title %}Index{% endblock %}

{% block html_head %}
  <style type="text/css">
    .important {
      color: #336699;
    }
  </style>
{% endblock %}

{% block content %}
    <h1>Index</h1>
    <p class="important">
      Welcome on my awsome homepage.
    </p>
{% endblock %}

Let's go over this since there are some important concepts being showcased here.

Understanding the inheritance: child template

The {% extends %} tag is the key here. It tells the template engine that this template "extends" another template. When the template system evaluates this template, first it locates the parent.

The filename of the template depends on the template loader. For example the FileSystemLoader allows you to access other templates by giving the filename. You can access templates in subdirectories with an slash:

{% extends "layout/default.html" %}

But this behavior can depend on the application using Jinja.

Note that since the child template didn't define the footer block, the value from the parent template is used instead.

Note also that we can't define multiple {% block %} tags with the same name in the same template. This limitation exists because a block tag works in "both" directions. That is, a block tag doesn't just provide a hole to fill - it also defines the content that fills the hole in the parent. If there were two similarly-named {% block %} tags in a template, that template's parent wouldn't know which one of the blocks' content to use.

If a template contains an {% extends %} tag it's considered being a child template, otherwise it's a layout template. In a layout template you can place blocks basically everywhere. In a child template blocks can only be located either at the top level or inside another block.

Data outside of a block in a child template is executed before the layout template is rendered, thus you can use it to propagate data to the whole inheritance chain. Having a block in an invalid position you will receive an syntax error. Here some examples:

impossible:

{% extends 'master.html' %}
{% if some_condition %}
  {% block body %}
    ...
  {% endblock %}
{% endif %}

This can't work because template inheritance works at translation / compilation time not at template execution.

possible:

{% extends 'master.html' %}
{% block body %}
  {% if some_condition %}
    {% block myblock %}
      ...
    {% endblock %}
  {% endblock %}
{% endblock %}

This can work although it probably makes no sense in this specific case. However the condition is handled at runtime because it's in a valid block and defines a new block subtemplates can override.

Applying inheritance to our application

In our application, we will apply template inheritance by creating a base component that contains the header, configuration for background images and the link to CSS files. We will also then configure the standard views for each application view (Register, Add recipe, Homepage, Recipe details,etc.) so that they can work under the newly introduced inheritance model.

So, the first step is to perform the extraction from each header contents of our files into a new file, called base_template.html:

<head>
    <!-- Latest compiled and minified CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <!-- Optional theme -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css"
          integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
    <!-- Latest compiled and minified JavaScript -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
</head>
<div class="topnav">
    <a class="active" href="/">Home</a>
    <a href="userRegistry"> Register </a>
    {% if not (current_user.is_authenticated and current_user.is_active)%}
    <div class="login-container">
        <form method="POST" action="login">
            <input type="text" placeholder="Email" name="email">
            <input type="password" placeholder="Password" name="password">
            <button type="submit">Login</button>
        </form>
    </div>
    {% else %}
        <a href="userRecipe"> Enter your own recipe </a>
        <p>Hi, {{ current_user.username }}</p>
    <a href="logout" class="btn btn-default" role="button" aria-pressed="true">Logout</a>
    {% endif %}

</div>

<div id="content">
    {% block content %}{% endblock %}
</div>

<body>
{% block body %}{% endblock %}
</body>

{% block scripts %}{% endblock %}

The most important thing here is the definition of the named blocks as discussed above. These blocks will be placeholders to serve the respective content on each of our children templates.

So, on every child, we will need to wrap the corresponding elements in these block tags, so that the inheritance can work, by using the base head element and knowing which "child block" elements to add, depending on which view we are in.

For instance, on our main recipe_list.html here are the template areas integrated in the child template:

<!DOCTYPE html>
<html lang="en">

{% extends "base_template.html" %}

{% block content %}
    <div>
        {% with messages = get_flashed_messages(with_categories=true) %}
...

Then we have the body:

{% block body %}
    <body>
    <form method="POST" autocomplete="off">
(...)
    </body>
{% endblock %}

And the scripts:

{% block scripts %}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
(...)
{% endblock %}

And, this is how we can leverage Jinja's powerful template inheritance to add a top navbar to our application.

Conclusion

If your web application will start from a certain common foundation, it can be a good idea to use the elements that comprise that foundation in a base template, and make your "child", more specialized views, to depend on those common elements. It will reduce the amount of HTML you need to write and increase the maintainability of your individual components!

Stay tuned for more updates!!

Top comments (0)