Version: v0.9.1 (Current)

Templates / Tera

Kalgan template system is based on tera crate for which it offers full support out of the box. Tera is a template engine for Rust based on Jinja2/Django. For a deeper knowledge of this crate and all its buil-in functions, please visit the official website or the GitHub site.

Configuring Tera

Firstly, we must add the feature tera in Cargo.toml:

...
[dependencies]
kalgan = { version = "0.9.1", features = ["tera"] }

And then we must set in our settings file the path of the root folder of our templates. For example:

...
tera:
    path: template
...

Notice this folder is located at the same level of our folder src, that it to say:

project/
    Cargo.toml
    config/
        app/
            settings.yaml
        routes/
            ...
    src/
        controller/
            foo_controller.rs
        controller.rs
        main.rs
        ...
    template/
        foo/
            foobar.html

And that's all, we don't have to worry about any other configuration task. Kalgan does job for us. :)

Last but not least, while we're developing (environment.is_prod: false) we must be aware that any change made in the templates is taken on the fly, without restarting the app. We just have to reload the browser.

Rendering the templates

Templates are called from the controllers. Kalgan offers several ways to do this (for more information see Controller Response in the docs). But for the sake of readability, we're going to use only the macro render!() in the following examples.

For example:

use kalgan::http::{ request::Request, response::Response };
pub(crate) fn foobar(request: &Request) -> Response {
    render!("foo/foobar.html")
}

In this example we're calling template foo/foobar.html. Notice that we don't have to indicate the root folder template we set in our settings file, Tera is already aware about it.

We haven't passed any parameters to our template. Let's see how to do it with another example:

use kalgan::http::{ request::Request, response::Response };
use kalgan::template::Context;

pub(crate) fn foobar(request: &Request) -> Response {
    let mut context = Context::new();
    context.insert("key_str", "Hello World!");
    context.insert("key_string", &"Hello World!".to_string());
    context.insert("key_num", &101);
    render!("foo/foobar.html", context)
}

Parameters are passed through the struct kalgan::template::Context. Notice that they must be passed by reference.

However we find this wayforward a bit verbose. Especially if we're coming from a different language. Let's add some sugar using kalgan::template::Sugar. For example:

use kalgan::http::{ request::Request, response::Response };
use kalgan::template::{Context, Sugar};

pub(crate) fn foobar(request: &Request) -> Response {
    render!("foo/foobar.html", Context::new()
        .add("key_str", "Hello World!")
        .add("key_string", &"Hello World!".to_string())
        .add("key_num", &101))
}

This looks much better. However we must be aware that there's a little performance drawback as with every method add we're cloning the Context.

However, we can even improve the previous example using the macro context!. For example:

use kalgan::http::{ request::Request, response::Response };

pub(crate) fn foobar(request: &Request) -> Response {
    render!("foo/foobar.html", context!(
        "key_str" => "Hello World!",
        "key_string" => &"Hello World!".to_string(),
        "key_num" => &101))
}

Built-in functions for Tera

Kalgan offers three custom filters for Tera templates:

|trans

It translates a message given its key. Parameters list is optional. However if our site supports more than one language we'll have to pass the parameter _lang. Otherwise messages will always be translated into the default language.

For example:

...
<h1>{{ "title"|trans }}</h1>
<h2>{{ "subtitle"|trans(_lang="en") }}</h2>
<p>{{ "hello.user"|trans(_lang="en", name="John", surname="Doe") }}</p>
...

For more information regarding message translation go to i18n support in routes in the docs.

|url

It generates the URI based in the key given. Parameters list is optional.

For example:

...
<a href="{{ "article_index"|url }}">Link to List of Articles</a>
<a href="{{ "article_read"|url(id=9) }}">Link to Article Number 9</a>
...

For more information regarding route generation go to Generating URIs in the docs.

|asset

While deveoloping (environment.is_prod: false), it appends at the end of the file the current timestamp: This prevents the browser from using the cached content of the static files. It also append at the begining the root folder location of the static files (if parameter static.path is defined in our settings file).

For example:

...
<link rel="stylesheet" href="{{ "/vendor/Font-Awesome/css/all.min.css"|asset }}">
<script src="{{ "/vendor/jquery/dist/jquery.min.js"|asset }}"></script>
...

For more information regarding static files go to Calling Assets in the docs.

Extending Tera

Although Kalgan sets up Tera by default, we are allowed to configure Tera up to our needs (new filters, functions, auto-escaping criteria, etc).

Find below the steps to set up Tera in Kalgan:

1. For example, create the file src/tera.rs as follows:

use std::collections::HashMap;
use kalgan::template::{Result, Tera, Value};

pub(crate) fn config(tera: &mut Tera) -> &mut Tera {
    tera.register_filter("my_filter", my_filter);
    tera
}
fn my_filter(value: &Value, parameters: &HashMap) -> Result {
    ...
}

In this example we have created a custom filter my_filter. The key function is config, which is the place were we're going to define our Tera instance, which is passed as a parameter.

2. In src/main.rs call kalgan::set_tera_config and pass the function config as a parameter:

...
mod tera;

fn main() {
    kalgan::set_tera_config(tera::config);
    kalgan::run(...);
}

And that's all. :)

A deeper explanation about the available options to config Tera is out of scope of these docs. If you need more information go to Advance usage in Tera Documentation.