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
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.
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 !".
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.
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"}
});
}