Añade ComponentError con HTML alternativo

`prepare_component()` ahora devuelve `Result<Markup, ComponentError>` en
lugar de `Markup`, para que los componentes señalen fallos durante el
renderizado de forma explícita.

`ComponentError` encapsula un mensaje de error y un marcado HTML
alternativo opcional (`fallback`). Si se produce un error, el ciclo de
renderizado registra la traza y muestra el `fallback` en lugar del
componente fallido, sin interrumpir el resto de la página.

Lo mismo aplica a los errores devueltos por la acción `PrepareRender` de
los temas, que siguen el mismo mecanismo.
This commit is contained in:
Manuel Cillero 2026-03-21 13:15:39 +01:00 committed by Manuel Cillero
parent 015b31733d
commit f51d90dd66
26 changed files with 232 additions and 100 deletions

View file

@ -36,20 +36,20 @@ impl Component for Brand {
self.id.get()
}
fn prepare_component(&self, cx: &mut Context) -> Markup {
fn prepare_component(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
let image = self.image().render(cx);
let title = self.title().using(cx);
if title.is_empty() && image.is_empty() {
return html! {};
return Ok(html! {});
}
let slogan = self.slogan().using(cx);
html! {
Ok(html! {
@if let Some(route) = self.route() {
a class="navbar-brand" href=(route(cx)) { (image) (title) (slogan) }
} @else {
span class="navbar-brand" { (image) (title) (slogan) }
}
}
})
}
}

View file

@ -48,7 +48,7 @@ impl Component for Navbar {
});
}
fn prepare_component(&self, cx: &mut Context) -> Markup {
fn prepare_component(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
// Botón de despliegue (colapso u offcanvas) para la barra.
fn button(cx: &mut Context, data_bs_toggle: &str, id_content: &str) -> Markup {
let id_content_target = util::join!("#", id_content);
@ -75,13 +75,13 @@ impl Component for Navbar {
// Si no hay contenidos, no tiene sentido mostrar una barra vacía.
let items = self.items().render(cx);
if items.is_empty() {
return html! {};
return Ok(html! {});
}
// Asegura que la barra tiene un `id` para poder asociarlo al colapso/offcanvas.
let id = cx.required_id::<Self>(self.id());
html! {
Ok(html! {
nav id=(id) class=[self.classes().get()] {
div class="container-fluid" {
@match self.layout() {
@ -162,7 +162,7 @@ impl Component for Navbar {
}
}
}
}
})
}
}

View file

@ -46,15 +46,15 @@ impl Component for Item {
}
}
fn prepare_component(&self, cx: &mut Context) -> Markup {
match self {
fn prepare_component(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
Ok(match self {
Self::Void => html! {},
Self::Brand(brand) => html! { (brand.render(cx)) },
Self::Nav(nav) => {
if let Some(nav) = nav.borrow() {
let items = nav.items().render(cx);
if items.is_empty() {
return html! {};
return Ok(html! {});
}
html! {
ul id=[nav.id()] class=[nav.classes().get()] {
@ -70,7 +70,7 @@ impl Component for Item {
(text.using(cx))
}
},
}
})
}
}