left-icon

Keystone.js Succinctly®
by Manikanta Panati

Previous
Chapter

of
A
A
A

CHAPTER 4

Templating with Swig

Templating with Swig


Swig is a simple, powerful, and extendable JavaScript template engine. A template engine is typically used to display data that has been returned from a query to the database. The template engine combines data and markup to generate an output that can be rendered by the browser. Swig’s syntax is very similar to many existing template engines such as Jinja2 and Django, used by other programming languages. If you are familiar with any of these technologies, you should find Swig really simple.

Some of the advantages of using Swig are that it:

  • Has an object-oriented template inheritance.
  • Applies filters and transformations to output in your templates.
  • Automatically escapes all output for safe HTML rendering.
  • Supports lots of iteration and conditionals.
  • Is robust, extendable, and customizable.

Swig templates can either end with a .swig or .html extension. You can control this in the keystone.init method in the keystone.js file in the root of the application.

Code Listing 34: Set up view engine

keystone.init({

    //use .html extension for template files

    'view engine': 'html',

});

All templates reside within /templates/views folder within the application.

Basic template operations

In a template language, variables passed to the template are replaced at predefined locations in the template. In Swig, variable substitutions are defined by {{ }}. There are also control blocks defined by {% %}, which declare language functions, such as loops or if statements.

To render the title of a news item, use the following notation.

Code Listing 35: Render a variable

{{ news.title }}

Swig follows the same rules as JavaScript. If a key includes non-alphanumeric characters, it must be accessed using bracket-notation, not dot-notation.

We can also invoke functions and render the output as if we were rendering a variable. If you recall, we introduced a virtual method named url in the previous chapter (see Code Listing 19). We can invoke the url method from the template and render a URL to the detail page for the news item.

Code Listing 36: Invoke a method from template

<a href=’{{ news.url() }}’> {{news.title}} </a>

To output comments, use a curly brace followed by the hash sign. Comments are removed by the parser during rendering, and will not be seen even if you do a view-source on the rendered HTML page.

Code Listing 37: Output code comments

{#

              This is a comment.

#}

Loops and control structures

Swig also provides convenient syntaxes for common control structures, such as conditional statements and loops. These shortcuts provide a very clean, terse way of working with control structures, while also remaining similar to their JavaScript counterparts.

If statements

You may construct if statements using the if, elif, else, and endif directives.

Code Listing 38: If conditionals

{% if length(newsitems) > 10 %}

    I have more than 10 records

{% elif length(newsitems) < 5 %}

    I have less than 5 records!

{% else %}

    I don't have any records!

{% endif %}

Boolean operators like and and or can be used within logic tags. Code Listing 39 is an example illustrating the use of the and conditionals.

Code Listing 39: Boolean operators

{% if length(news.title) > 10 and length(news.content) > 10 %}

    {{news.title}} can be displayed.

{% endif %}

We can also use built-in JavaScript functions within the conditional statements.

Code Listing 40: Use JS functions in conditionals

{% if news.title.indexOf(“critical”) > -1 %}

    This is a critical news item

{% endif %}

Loop statements:

Swig also supports looping statements to iterate over arrays and objects. To iterate over the tags array in the news item object, add the following markup to the template.

Code Listing 41: Loop statements

<ul>

{% for tag in newsitem.tags %}

<li> {{tag}} </li>

{% endfor %}

</ul>

Swig has a collection of very helpful loop control helpers. These provide additional information about the state of the loop in an iteration.

Code Listing 42: Loop control helpers

{% for tag in data.ticket.tags %}

    {% if loop.first %}<ul>{% endif %}

        <li>{{ loop.index }} - {{ tag }}</li>

    {% if loop.last %}</ul>{% endif %}

{% endfor %}

During every for loop iteration, the following helper variables are available:

  • loop.index: The current iteration of the loop (1-indexed).
  • loop.index0: The current iteration of the loop (0-indexed).
  • loop.revindex: The number of iterations from the end of the loop (1-indexed).
  • loop.revindex0: The number of iterations from the end of the loop (0-indexed).
  • loop.key: If the iterator is an object, this will be the key of the current item; otherwise it will be the same as the loop.index.
  • loop.first: True if the current item is the first in the object or array.
  • loop.last: True if the current item is the last in the object or array.

To reverse a loop, we can use the reverse filter:

Code Listing 43: Reverse loop

<ul>

{% for tag in newsitem.tags | reverse %}

<li> {{tag}} </li>

{% endfor %}

</ul>

Swig built-in filters

Filters are methods through which output can be manipulated before rendering. Filters are special functions that are applied after any object token in a variable block using the pipe character (|). Filters can also be chained together, one after another.

If, for example, we wanted to convert the title of a news item to title case and strip any HTML tags that might have been input, we can use the title and striptags filters.

Code Listing 44: Use filters in templates

<div>

    News Title - {{newsItem.title | title | striptags}}

</div>

Here is a list of the 23 available filters in Swig:

  • capitalize: Capitalizes words in the input.
  • lower: Converts an input string to lowercase.
  • upper: Converts an input string to uppercase.
  • title: Capitalizes every word given and lower-cases all other letters in input.
  • date: Reformats a date.
  • default: A default return value can be specified if the input is undefined, null, or false.
  • json: Converts input to JavaScript object.
  • striptags: Strips HTML from string.
  • safe: Forces the input to not be auto-escaped. Swig escapes data by default.
  • replace: Replaces each occurrence of a string.
  • escape: Escapes special characters in a string.
  • addslashes: Adds backslashes to characters that need to be escaped.
  • url_encode: URL-encodes a string.
  • url_decode: URL-decodes a string.
  • first: Gets the first element of the input array, object, or string.
  • last: Gets the last element of the input array, object, or string.
  • reverse: Reverse-sorts the input values.
  • sort: Sorts the input in an ascending order.
  • join: Joins elements of the array with a delimiter.
  • groupBy: Groups an array of objects by a key.
  • uniq: Removes all duplicate elements from an array.
  • length: Gets the number of items in an array.
  • raw: Similar to safe; prevents data from being auto-escaped.

Template inheritance

Since most web applications maintain the same general layout across various pages, it's convenient to define this layout as a single Swig template and use some kind of template inheritance/injection to be able to render specific partial templates. Swig makes this easy with extends and block directives.

Create a template named layout.swig and save it under the /templates/layouts folder with the following content.

Code Listing 45: Base layout template

<!doctype html>

<html>

<head>

  <meta charset="utf-8">

  <title>{% block title %}NodePress{% endblock %}</title>

  {% block head %}

  <link rel="stylesheet" href="main.css">

  {% endblock %}

</head>

<body>

  {% block content %}{% endblock %}

</body>

</html>

Next, to use the base template in another template, create a template named news.swig and save it in the templates/views directory with the following content.

Code Listing 46: Extend base layout template

{% extends 'layout.html' %}

{% block title %}News Detail Page{% endblock %}

{% block content %}

<p>We will display the news details here</p>

{% endblock %}

Template partials

Templates can easily get bulky and difficult to maintain if we do not organize the contents in a good manner. An easy way to organize the different sections of a template is to use template partials. Template partials are pieces of templates that reside in separate files and are combined together to make a single template.

For example, the preceding news layout template can leverage multiple partials that are concerned with displaying the header and footer of the pages.

Code Listing 47: Header template

<!doctype html>

<html>

<head>

  <meta charset="utf-8">

  <title>{% block title %}NodePress{% endblock %}</title>

  {% block head %}

  <link rel="stylesheet" href="main.css">

  {% endblock %}

</head>

Code Listing 48: Footer template

<footer>

&copy; nodepress 2016. All rights reserved.

</footer>

</html>

Code Listing 49: Updated base template

{% include 'header.swig' %}

<body>

  {% block content %}{% endblock %}

</body>

{% include 'footer.swig' %}

Another recommended scenario in which to use template partials would be within loops. The code will be much easier to understand and maintain.

Code Listing 50: Template partial in loops

<table class="table table-striped">

    {% for news in data.newsitems %}

        {% include 'news.swig' %}

    {% endfor %}

</table>

Macros

A macro is a function in Swig that returns a template or HTML string. This is used to avoid code that is repeated over and over again and reduce it to one function call. For example, the following is a macro to show a news item.

Code Listing 51: Macro in template

{% macro showNews(news) %}

<div class="post">

    <h2>

        <a href="{{ news.url() }}">{{ news.title }}</a>

    </h2>

    <p>Posted

{% if news.publishedDate %}

            <br>on {{ news._.publishedDate.format("MMMM Do, YYYY") }}

{% endif %}

</p>

</div>

{% endmacro %}

Now, to quickly display a news item in any template, call your macro using the following.

Code Listing 52: Invoke macro

{{ showNews(newsObj) }}

Summary

In this chapter, you learned about Swig, the powerful and flexible templating option for Node.js. There are other similar frameworks, and I would suggest you play around with a few to find the one that best fits your style of coding.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.