Creación de Templates Dinámicos con Tera en Rust
Clase 17 de 21 • Curso de Backend con Rust: Bases de Datos, Controladores y Templates
Resumen
Puedes crear tantas páginas HTML como tu aplicación necesite. En cualquier página web es fundamental poder enviar información dinámicamente al template.
Pasaje de variables al template
Vamos a crear una página HTML para mostrar todos los registros. Para esto, modifica el index de tu aplicación para que devuelva un “contexto” y lograr pasarle datos a tu HTML.
#[get("/")]
async fn index(pool: web::Data<DbPool>, template_manager: web::Data<tera::Tera>) -> impl Responder {
let conn = pool.get().expect("Problemas al traer la base de datos");
// Consulta para obtener todos los registros
match web::block(move || {posts.load::<Post>(&conn)}).await {
Ok(data) => {
let data = data.unwrap();
// Enviamos, a través del contexto, los datos al HTML
let mut ctx = tera::Context::new();
ctx.insert("posts", &data);
// Pasamos los datos al template index.html
HttpResponse::Ok().content_type("text/html").body(
template_manager.render("index.html", &ctx).unwrap()
)
},
Err(err) => HttpResponse::Ok().body("Error al recibir la data")
}
}
En cuanto al HTML, Tera utiliza un lenguaje especial y muy fácil de utilizar para mostrar los datos o crear un ciclo for.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
{% for post in posts %}
<div>
<h1>
<a href="/blog/{{post.slug}}">{{ post.title }} </a>
</h1>
<p>{{ post.body }}</p>
</div>
{% endfor %}
</body>
</html>
A la vez que mostramos el título y la descripción del registro, estamos generando un enlace HTML para redireccionar a la página exclusiva del registro.
Captura de parámetros
Para crear una página exclusiva para cada registro, normalmente se pasa un ID o una clave única del mismo a través de los parámetros de URL. Vamos a crear una nueva página para capturar esta información y renderizar un solo registro.
// Capturamos el parámetro por URL
#[get("/blog/{blog_slug}")]
async fn get_post(
pool: web::Data<DbPool>,
template_manager: web::Data<tera::Tera>,
blog_slug: web::Path<String>
) -> impl Responder {
let conn = pool.get().expect("Problemas al traer la base de datos");
let url_slug = blog_slug.into_inner();
match web::block(move || {posts.filter(slug.eq(url_slug)).load::<Post>(&conn)}).await {
Ok(data) => {
let data = data.unwrap();
// Si el post no existe, devolvemos 404
if data.len() == 0 {
return HttpResponse::NotFound().finish();
}
let data = &data[0];
// Enviamos, a través del contexto, los datos del post al HTML
let mut ctx = tera::Context::new();
ctx.insert("post", data);
HttpResponse::Ok().content_type("text/html").body(
template_manager.render("post.html", &ctx).unwrap()
)
},
Err(err) => HttpResponse::Ok().body("Error al recibir la data")
}
}
El URL está compuesto por una variable dinámica /blog/{blog_slug}
la cual podemos capturar como parámetro gracias a blog_slug: web::Path<String>
. Con este dato, buscamos y devolvemos el registro correspondiente o devolvemos 404 si el mismo no existe.
Mientras que en el HTML, solo muestras la información de un solo registro.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ post.title }}</title>
</head>
<body>
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.body }}</p>
<a href="/">Volver</a>
</div>
</body>
</html>
De esta forma, ya sabes exponer un servidor HTTP con Rust y Actix, conectarte a una base de datos SQL con Diesel y renderizar la información en un template HTML con Tera. Hemos completado todo el stack necesario para desarrollar tu primera página web con Rust. ¡Felicidades!
Contribución creada por: Kevin Fiorentino.