- Add a comprehensive .gitignore to manage project files. - Create the basic project structure including Cargo.toml, LICENSE, and README.md. - Add basic project documentation.
6.1 KiB
Views Guide
This guide provides detailed instructions on how to create and customize views in the Hostbasket application.
Table of Contents
- Introduction to Tera Templates
- Template Structure
- Creating a New View
- Template Inheritance
- Using Variables
- Control Structures
- Filters
- Macros
- Custom Functions
- Best Practices
Introduction to Tera Templates
Hostbasket uses Tera as its template engine. Tera is inspired by Jinja2 and Django templates and provides a powerful way to create dynamic HTML pages.
Template Structure
Templates are stored in the src/views
directory. The directory structure typically follows the application's features:
src/views/
├── auth/
│ ├── login.html
│ └── register.html
├── home/
│ ├── index.html
│ ├── about.html
│ └── contact.html
├── tickets/
│ ├── index.html
│ ├── new.html
│ └── show.html
├── assets/
│ ├── index.html
│ ├── create.html
│ └── detail.html
└── base.html
Creating a New View
To create a new view:
- Create a new HTML file in the appropriate directory under
src/views
- Use template inheritance to extend the base template
- Add your content within the appropriate blocks
Example:
{% extends "base.html" %}
{% block title %}My New Page{% endblock %}
{% block content %}
<div class="container">
<h1>Welcome to My New Page</h1>
<p>This is a custom page.</p>
</div>
{% endblock %}
Template Inheritance
Tera supports template inheritance, which allows you to define a base template with common elements and extend it in child templates.
Base Template
The base template (base.html
) typically contains the HTML structure, header, footer, and navigation menu:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Hostbasket{% endblock %}</title>
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/css/styles.css">
{% block head %}{% endblock %}
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<!-- Navigation content -->
</nav>
<main>
{% block content %}{% endblock %}
</main>
<footer class="footer mt-auto py-3 bg-light">
<!-- Footer content -->
</footer>
<script src="/static/js/bootstrap.bundle.min.js"></script>
{% block scripts %}{% endblock %}
</body>
</html>
Child Template
Child templates extend the base template and override specific blocks:
{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block content %}
<div class="container">
<h1>Welcome to Hostbasket</h1>
<p>This is the home page.</p>
</div>
{% endblock %}
{% block scripts %}
<script src="/static/js/home.js"></script>
{% endblock %}
Using Variables
You can pass variables from your controller to the template and use them in your HTML:
In the Controller
pub async fn index(tmpl: web::Data<Tera>) -> Result<impl Responder> {
let mut ctx = tera::Context::new();
ctx.insert("title", "Welcome to Hostbasket");
ctx.insert("user_name", "John Doe");
render_template(&tmpl, "home/index.html", &ctx)
}
In the Template
<h1>{{ title }}</h1>
<p>Hello, {{ user_name }}!</p>
Control Structures
Tera provides various control structures for conditional rendering and iteration.
Conditionals
{% if user %}
<p>Welcome, {{ user.name }}!</p>
{% else %}
<p>Please log in.</p>
{% endif %}
Loops
<ul>
{% for item in items %}
<li>{{ item.name }}</li>
{% endfor %}
</ul>
{% if items is empty %}
<p>No items found.</p>
{% endif %}
Filters
Filters transform the values of variables:
<p>{{ user.name | upper }}</p>
<p>{{ user.bio | truncate(length=100) }}</p>
<p>{{ user.created_at | date(format="%Y-%m-%d") }}</p>
Macros
Macros are reusable template fragments:
{% macro input(name, value='', type='text', label='') %}
<div class="mb-3">
{% if label %}
<label for="{{ name }}" class="form-label">{{ label }}</label>
{% endif %}
<input type="{{ type }}" name="{{ name }}" id="{{ name }}" value="{{ value }}" class="form-control">
</div>
{% endmacro %}
<!-- Usage -->
{{ input(name="email", type="email", label="Email Address") }}
Custom Functions
You can register custom functions in Rust and use them in your templates:
In Rust
fn register_tera_functions(tera: &mut Tera) {
tera.register_function("format_date", format_date);
}
fn format_date(args: &HashMap<String, Value>) -> Result<Value, tera::Error> {
// Implementation
}
In the Template
<p>{{ format_date(date=user.created_at, format="%B %d, %Y") }}</p>
Best Practices
- Use Template Inheritance: Extend the base template to maintain consistency across pages.
- Organize Templates by Feature: Group related templates in subdirectories.
- Keep Templates Simple: Move complex logic to the controller or custom functions.
- Use Meaningful Variable Names: Choose descriptive names for variables and blocks.
- Comment Your Templates: Add comments to explain complex sections.
- Validate User Input: Always validate and sanitize user input in the controller before passing it to the template.
- Use Partials for Reusable Components: Extract common components into separate files and include them where needed.
- Optimize for Performance: Minimize the use of expensive operations in templates.
- Test Your Templates: Ensure that your templates render correctly with different data.
- Follow Accessibility Guidelines: Make your templates accessible to all users.