1. Rendering

1.1 Creating a view

Crails views are created from a view template, stored in the app/views folder. They are then compiled to C++ using a code generator defined in the Guardfile, and can then be rendered from your application.

Templates can exist in several formats: HTML and JSON support can be added to your application by installing the crails-html and crails-json modules, using the crails module html install abd crails module json install.

Create the file app/views/home/index.html as following:

<p>Hi HTML template !</p>

You may compile your template by calling the crails build command, which will run all the code generators.

1.2 Rendering a view

Views are typically rendered from a controller, using the Crails::Controller::render method, as following:

#ifndef  HOME_CONTROLLER_HPP
# define HOME_CONTROLLER_HPP

# include <crails/controller.hpp>

class HomeController : public Crails::Controller
{
public:
  HomeController(Crails::Context& context) : Crails::Controller(context)
  {}

  void index()
  {
    render("home/index"); // will render app/views/home/index.ecpp
  }
};

#endif
The controller's renderer uses the request's Accept header to determine which renderer to use. If no template were provided for any of the formats specified in the Accept header, the Crails::MissingTemplate exception will be thrown.

You can also directly render a template using a Renderer directly. For the HtmlRenderer, here's how we would proceed:

#ifndef  HOME_CONTROLLER_HPP
# define HOME_CONTROLLER_HPP

# include <crails/controller.hpp>
# include <crails/renderers/html_renderer.hpp>

class HomeController : public Crails::Controller
{
public:
  HomeController(Crails::Context& context) : Crails::Controller(context)
  {}

  void index()
  {
    Crails::HtmlRenderer().render_template("home/index", this->response);
  }
};

#endif

Instead of relying on the controller's renderer, we explicitely instantiate an HtmlRenderer and call render_template, which takes two parameters: the template's name, and the target in which the view will be rendered.

1.3 Rendering a view to a string

The previous method renders the view directly to the HTTP response object. But you may also want to render a view without necessarily sending it as a response. In the following example, we'll store the result in a RenderString before setting a response:

void index()
{
  Crails::RenderString result;

  Crails::HtmlRenderer().render_template("home/index", result);
  response.set_body(result.value().data(), result.value().length());
}

RenderString::value() returns a std::string_view containing the rendered view.

2. Templates

2.1 Linking section

View templates may start with a linking section, delimited by the // END LINKING separator. This section is where you will declare include files, namespaces, typedefs and variables required by your template.

using namespace std;

string message = "Hi HTML template !";
// END LINKING
<p><%= message %></p>

In this example, we declare a local stirng variable named message, and initialize it with a default value. We then render that string within the view using the <%= ... %> delimiters.

You should not try to use the preprocessor from the linking section.

2.2 Shared variables

Views can import variables from other contexts in the linking section of a template. Let's now edit our previous template to import the value of the message variable from another context:

using namespace std;

string @message = "Hi HTML template !";
// END LINKING
<p><%= message %></p>

In this example, we added an @ character before the variable's name. This means that this variable's value should be provided by the renderer.

If the renderer doesn't provide the variable, then the default initializer will be used, and our message will be "Hi HTML template !".

Note that if we do not provide a default initializer to a shared variable, then trying to render the template without providing a value for the variable will result in an out_of_range exception.
Similarly, if you provide a value to the variable, but the typeid does not match the one used in the template, a runtime_error exception will be thrown.

2.3 Sharing variables with a view

When rendered from a Crails controller, views will use the controller's shared variable set by default, which is accessible from the controller as Crails::Controller::vars.

Here's the simplest way to share variables with a view:

#ifndef  HOME_CONTROLLER_HPP
# define HOME_CONTROLLER_HPP

# include <crails/controller.hpp>

class HomeController : public Crails::Controller
{
public:
  HomeController(Crails::Context& context) : Crails::Controller(context)
  {}

  void index()
  {
    vars["message"] = "Howdy, little birdie !";
    render("home/index");
  }
};

#endif

Note that in this example, "Howdy, little birdie !" has the const char* type, which does not match the variable std::string type we set for the message variable in our template.
With any other types, this would be a mistake: you need to be careful that the rvalue you assign to a shared variable is exactly the same as the template's. However, Crails will automatically convert between std::string, std::string_view and const char*, making this example valid.

Here is another way to pass variables to a view:

#ifndef  HOME_CONTROLLER_HPP
# define HOME_CONTROLLER_HPP

# include <crails/controller.hpp>

class HomeController : public Crails::Controller
{
public:
  HomeController(Crails::Context& context) : Crails::Controller(context)
  {}

  void index()
  {
    render("home/index", {
      {"message", "Howdy, little birdie !"}
    });
  }
};

#endif

In this example, we send our variables directly to the controller's renderer. The controller's own variable will still be shared with the views, but the variables sent as parameter will also be available. When some variable names overlap, the ones sent as parameters will override the controller's.

2.4 Partial rendering

With more complex application, you may end up needing to render views within other views. This can be done through partial rendering:

using namespace std;

string message = "Hi HTML template !";
// END LINKING
<h1><%= message %></h1>
<%= partial("home/partial"); %>

You may also need to send custom variables to the partial view. This can easily be achieved using the optional second parameter for the partial method:

using namespace std;

string message = "Hi HTML template !";
// END LINKING
<h1><%= message %></h1>
<%= partial("home/partial", {{"variable", message.length()}); %>

3. Layouts

3.1 Creating a layout

Layouts are a simple helper that you can use to have all the views from a controller be rendered within a layout view. Here's what a layout view may look like:

const char* @yield;
// END LINKING
<html>
  <head>
    <title>My application</title>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

Layout template are provided with a yield shared variable, which will contain a rendered view that you can incorporate within the layout.

In addition to the yield variable, the layout also gets access to all the shared variables sent to the view you're rendering.

3.2 Setting a default layout

Each controller can set a default layout by setting the layout shared variable, as such:

#ifndef  HOME_CONTROLLER_HPP
# define HOME_CONTROLLER_HPP

# include <crails/controller.hpp>

class HomeController : public Crails::Controller
{
public:
  HomeController(Crails::Context& context) : Crails::Controller(context)
  {
    vars["layout"] = "home/my_layout";
  }

  void index()
  {
    render("home/index");
  }
};

#endif

Alternatively, you can also provide or override the layout by sending a variable directly to the renderer:

  void index()
  {
    render("home/index", {
      {"layout", "home/my_layout"}
    });
  }