Views
In Loco
, the processing of web requests is divided between a controller, model and view.
- The controller is handling requests parsing payload, and then control flows to models
- The model primarily deals with communicating with the database and executing CRUD operations when required. As well as modeling all business and domain logic and operations.
- The view takes on the responsibility of assembling and rendering the final response to be sent back to the client.
You can choose to have JSON views, which are JSON responses, or Template views which are powered by a template view engine and eventually are HTML responses. You can also combine both.
JSON views
As an example we have an endpoint that handles user login. When the user is valid we can pass the user
model into the LoginResponse
view (which is a JSON view) to return the response.
There are 3 steps:
- Parse, accept the request
- Create domain objects: models
- Hand off the domain model to a view object which shapes the final response
The following Rust code represents a controller responsible for handling user login requests, which handes off shaping of the response to LoginResponse
.
use crate::;
async
On the other hand, LoginResponse
is a response shaping view, which is powered by serde
:
use ;
use crate users;
Template views
When you want to return HTML to the user, you use server-side templates. This is similar to how Ruby's erb
works, or Node's ejs
, or PHP for that matter.
For server-side templates rendering we provide the built in TeraView
engine which is based on the popular Tera template engine.
ViewEngineInitializer
in initializers/view_engine.rs
which is also specified in your app.rs
. If you used the SaaS Starter, this should already be configured for you.
The Tera view engine takes resources from the new assets/
folder. Here is an example structure:
assets/
├── i18n
│ ├── de-DE
│ │ └── main.ftl
│ ├── en-US
│ │ └── main.ftl
│ └── shared.ftl
├── static
│ ├── 404.html
│ └── image.png
└── views
└── home
└── hello.html
config/
:
src/
├── controllers/
├── models/
:
└── views/
Creating a new view
First, create a template. In this case we add a Tera template, in assets/views/home/hello.html
. Note that assets/ sits in the root of your project (next to src/
and config/
).
find this tera template at assets/views/home/hello.html :
{{ /* t(key="hello-world", lang="en-US") */ }},
{{ /* t(key="hello-world", lang="de-DE") */ }}
Now create a strongly typed view
to encapsulate this template in src/views/dashboard.rs
:
// src/views/dashboard.rs
use *;
And add it to src/views/mod.rs
:
Next, go to your controller and use the view:
// src/controllers/dashboard.rs
use *;
use crate views;
pub async
Finally, register your new controller's routes in src/app.rs
;
Once you've done all the above, you should be able to see your new routes when running cargo loco routes
$ cargo loco routes
[GET] /_health
[GET] /_ping
[POST] /api/auth/forgot
[POST] /api/auth/login
[POST] /api/auth/register
[POST] /api/auth/reset
[POST] /api/auth/verify
[GET] /api/auth/current
[GET] /home <-- the corresponding URL for our new view
How does it work?
ViewEngine
is an extractor that's available to you vialoco_rs::prelude::*
TeraView
is the Tera view engine that we supply with Loco also available vialoco_rs::prelude::*
- Controllers need to deal with getting a request, calling some model logic, and then supplying a view with models and other data, not caring about how the view does its thing
views::dashboard::home
is an opaque call, it hides the details of how a view works, or how the bytes find their way into a browser, which is a Good Thing- Should you ever want to swap a view engine, the encapsulation here works like magic. You can change the extractor type:
ViewEngine<Foobar>
and everything works, becausev
is eventually just aViewRenderer
trait
Static assets
If you want to serve static assets and reference those in your view templates, you can use the Static Middleware, configure it this way:
static:
enable: true
must_exist: true
precompressed: false
folder:
uri: "/static"
path: "assets/static"
fallback: "assets/static/404.html"
In your templates you can refer to static resources in this way:
However, for the static middleware to work, ensure that the default fallback is disabled:
fallback:
enable: false
Customizing the Tera view engine
The Tera view engine comes with the following configuration:
- Template loading and location:
assets/**/*.html
- Internationalization (i18n) configured into the Tera view engine, you get the translation function:
t(..)
to use in your templates
If you want to change any configuration detail for the i18n
library, you can go and edit src/initializers/view_engine.rs
.
By editing the initializer you can:
- Add custom Tera functions
- Remove the
i18n
library - Change configuration for Tera or the
i18n
library - Provide a new or custom, Tera (maybe a different version) instance
Using your own view engine
If you do not like Tera as a view engine, or want to use Handlebars, or others you can create your own custom view engine very easily.
Here's an example for a dummy "Hello" view engine. It's a view engine that always returns the word hello.
// src/initializers/hello_view_engine.rs
use ;
use async_trait;
use ;
use Serialize;
;
;
To use it, you need to add it to your src/app.rs
hooks:
// src/app.rs
// add your custom "hello" view engine in the `initializers(..)` hook
Tera Built-ins
Loco includes Tera with its built-ins functions. In addition, Loco introduces the following custom built-in functions:
To see Loco built-in function:
Embedded Assets Feature
The Embedded Assets feature in Loco allows you to bundle all your static assets directly into your application binary. This means that everything under the assets
folder, including CSS, images, PDFs, and more, becomes part of a single executable file.
To use this feature, you need to enable the embedded_assets
feature when importing loco-rs
in your Cargo.toml
:
[]
= { = "...", = ["embedded_assets"] }
Benefits
- Single Binary Deployment: Simplifies deployment as you only need to distribute a single file. No need to worry about separate asset directories or CDN configurations for simpler deployments.
- Atomic Updates: When you update your application, the assets are updated atomically with the code, reducing the chances of mismatches between code and assets.
- Potentially Faster Load Times: Assets are loaded directly from memory, which can be faster than reading from the filesystem, especially in environments with slow disk I/O.
Considerations
- Increased Binary Size: Embedding assets will naturally increase the size of your application binary.
- Recompilation for Asset Changes: Any change to an asset requires recompiling the application. This might slow down development workflows if assets are changed frequently.
Seamlessly Switching Modes
You can easily switch between using embedded assets and serving assets from the filesystem without any code changes in your controllers or views. The switch is handled by the presence or absence of the embedded_assets
feature flag.
However, to ensure Tera functions correctly when not using embedded assets (i.e., serving from the filesystem), you need to ensure that your src/initializers/view_engine.rs
file only contains the necessary Tera function registration if you had customized it previously. Specifically, for the translation function t
, ensure your initializer looks like this if you are not using loco_rs::tera_helpers::FluentLoader
:
tera_engine
.tera
.register_function;
Alternatively, you can introduce an internal feature flag within your application to toggle how assets are loaded or how Tera is configured, providing more granular control.
Build Time Logs
When you build your application with the embedded_assets
feature enabled, Loco will scan your assets
directory and embed the discovered files. You will see logs similar to the following during the build process, indicating which assets are being included:
warning: loco-rs@0.15.0: Assets will only be loaded from the application directory
warning: loco-rs@0.15.0: Discovered directories for assets:
warning: loco-rs@0.15.0: - /path/to/your/myapp/assets
warning: loco-rs@0.15.0: - /path/to/your/myapp/assets/static
warning: loco-rs@0.15.0: - /path/to/your/myapp/assets/i18n
warning: loco-rs@0.15.0: - /path/to/your/myapp/assets/i18n/de-DE
warning: loco-rs@0.15.0: - /path/to/your/myapp/assets/i18n/en-US
warning: loco-rs@0.15.0: - /path/to/your/myapp/assets/views
warning: loco-rs@0.15.0: - /path/to/your/myapp/assets/views/home
warning: loco-rs@0.15.0: Found asset: /path/to/your/myapp/assets/static/styles.css -> /static/styles.css
warning: loco-rs@0.15.0: Found asset: /path/to/your/myapp/assets/static/dummy.pdf -> /static/dummy.pdf
warning: loco-rs@0.15.0: Found asset: /path/to/your/myapp/assets/static/404.html -> /static/404.html
warning: loco-rs@0.15.0: Found asset: /path/to/your/myapp/assets/views/base.html -> base.html
warning: loco-rs@0.15.0: Found 13 asset files
warning: loco-rs@0.15.0: Generated code for 6 static assets and 7 templates
This output confirms that Loco has found your asset files (like CSS, PDFs, HTML templates) and has generated the necessary code to embed them into the binary. The paths will reflect your project's structure.