Creación de Templates Dinámicos con Tera en Rust

Clase 17 de 21Curso 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.