♻️ (pagetop): Refactorización de Contextual
Los métodos `required_id()` y `push_message()` son operaciones de tiempo de renderizado, no de construcción. Se trasladan como métodos inherentes de `Context`. También se ajustan los métodos asociados a parámetros.
This commit is contained in:
parent
318d7de2b6
commit
0419658192
7 changed files with 121 additions and 189 deletions
|
|
@ -79,10 +79,10 @@ impl Component for Navbar {
|
|||
}
|
||||
|
||||
// Asegura que la barra tiene un `id` para poder asociarlo al colapso/offcanvas.
|
||||
let id = cx.required_id::<Self>(self.id());
|
||||
let id = cx.required_id::<Self>(self.id(), 1);
|
||||
|
||||
Ok(html! {
|
||||
nav id=(id) class=[self.classes().get()] {
|
||||
nav id=(&id) class=[self.classes().get()] {
|
||||
div class="container-fluid" {
|
||||
@match self.layout() {
|
||||
// Barra más sencilla: sólo contenido.
|
||||
|
|
@ -95,7 +95,7 @@ impl Component for Navbar {
|
|||
@let id_content = util::join!(id, "-content");
|
||||
|
||||
(button(cx, TOGGLE_COLLAPSE, &id_content))
|
||||
div id=(id_content) class="collapse navbar-collapse" {
|
||||
div id=(&id_content) class="collapse navbar-collapse" {
|
||||
(items)
|
||||
}
|
||||
},
|
||||
|
|
@ -112,7 +112,7 @@ impl Component for Navbar {
|
|||
|
||||
(brand.render(cx))
|
||||
(button(cx, TOGGLE_COLLAPSE, &id_content))
|
||||
div id=(id_content) class="collapse navbar-collapse" {
|
||||
div id=(&id_content) class="collapse navbar-collapse" {
|
||||
(items)
|
||||
}
|
||||
},
|
||||
|
|
@ -123,7 +123,7 @@ impl Component for Navbar {
|
|||
|
||||
(button(cx, TOGGLE_COLLAPSE, &id_content))
|
||||
(brand.render(cx))
|
||||
div id=(id_content) class="collapse navbar-collapse" {
|
||||
div id=(&id_content) class="collapse navbar-collapse" {
|
||||
(items)
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ impl Offcanvas {
|
|||
return html! {};
|
||||
}
|
||||
|
||||
let id = cx.required_id::<Self>(self.id());
|
||||
let id = cx.required_id::<Self>(self.id(), 1);
|
||||
let id_label = util::join!(id, "-label");
|
||||
let id_target = util::join!("#", id);
|
||||
|
||||
|
|
@ -171,7 +171,7 @@ impl Offcanvas {
|
|||
|
||||
html! {
|
||||
div
|
||||
id=(id)
|
||||
id=(&id)
|
||||
class=[self.classes().get()]
|
||||
tabindex="-1"
|
||||
data-bs-scroll=[body_scroll]
|
||||
|
|
@ -180,7 +180,7 @@ impl Offcanvas {
|
|||
{
|
||||
div class="offcanvas-header" {
|
||||
@if !title.is_empty() {
|
||||
h5 class="offcanvas-title" id=(id_label) { (title) }
|
||||
h5 id=(&id_label) class="offcanvas-title" { (title) }
|
||||
}
|
||||
button
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -36,10 +36,10 @@ impl Component for Block {
|
|||
return Ok(html! {});
|
||||
}
|
||||
|
||||
let id = cx.required_id::<Block>(self.id());
|
||||
let id = cx.required_id::<Self>(self.id(), 1);
|
||||
|
||||
Ok(html! {
|
||||
div id=(id) class=[self.classes().get()] {
|
||||
div id=(&id) class=[self.classes().get()] {
|
||||
@if let Some(title) = self.title().lookup(cx) {
|
||||
h2 class="block__title" { span { (title) } }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ use std::sync::Arc;
|
|||
/// ```rust
|
||||
/// # use pagetop::prelude::*;
|
||||
/// let component = Html::with(|cx| {
|
||||
/// let user = cx.param::<String>("username").cloned().unwrap_or("visitor".to_string());
|
||||
/// let user = cx.param_or("username", "visitor".to_string());
|
||||
/// html! {
|
||||
/// h1 { "Hello, " (user) }
|
||||
/// }
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ pub use context::{AssetsOp, Context, ContextError, Contextual};
|
|||
///
|
||||
/// // Se instancia un componente que sólo se renderiza si `user_logged_in` es `true`.
|
||||
/// let mut component = SampleComponent::new().with_renderable(Some(|cx: &Context| {
|
||||
/// cx.param::<bool>("user_logged_in").copied().unwrap_or(false)
|
||||
/// cx.param_or_default::<bool>("user_logged_in")
|
||||
/// }));
|
||||
///
|
||||
/// // Aquí simplemente se comprueba que compila y se puede invocar.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::core::component::{ChildOp, MessageLevel, StatusMessage};
|
||||
use crate::core::component::{ChildOp, Component, MessageLevel, StatusMessage};
|
||||
use crate::core::theme::all::DEFAULT_THEME;
|
||||
use crate::core::theme::{ChildrenInRegions, DefaultRegion, RegionRef, TemplateRef, ThemeRef};
|
||||
use crate::core::TypeInfo;
|
||||
|
|
@ -77,7 +77,6 @@ impl std::error::Error for ContextError {}
|
|||
/// - Administrar **recursos** del documento como el icono [`Favicon`], las hojas de estilo
|
||||
/// [`StyleSheet`] o los scripts [`JavaScript`] mediante [`AssetsOp`].
|
||||
/// - Leer y mantener **parámetros dinámicos tipados** de contexto.
|
||||
/// - Generar **identificadores únicos** por tipo de componente.
|
||||
///
|
||||
/// Lo implementan, típicamente, estructuras que manejan el contexto de renderizado, como
|
||||
/// [`Context`](crate::core::component::Context) o [`Page`](crate::response::page::Page).
|
||||
|
|
@ -94,7 +93,7 @@ impl std::error::Error for ContextError {}
|
|||
/// .with_assets(AssetsOp::SetFavicon(Some(Favicon::new().with_icon("/favicon.ico"))))
|
||||
/// .with_assets(AssetsOp::AddStyleSheet(StyleSheet::from("/css/app.css")))
|
||||
/// .with_assets(AssetsOp::AddJavaScript(JavaScript::defer("/js/app.js")))
|
||||
/// .with_param("usuario_id", 42_i32)
|
||||
/// .with_param("user_id", 42_i32)
|
||||
/// }
|
||||
/// ```
|
||||
pub trait Contextual: LangId {
|
||||
|
|
@ -118,16 +117,17 @@ pub trait Contextual: LangId {
|
|||
|
||||
/// Añade o modifica un parámetro dinámico del contexto.
|
||||
///
|
||||
/// El valor se guardará conservando el *nombre del tipo* real para mejorar los mensajes de
|
||||
/// error posteriores.
|
||||
/// El valor se almacena junto con el nombre de su tipo, lo que permite generar mensajes de
|
||||
/// error precisos al recuperarlo con [`param`](Contextual::param) si el tipo solicitado no
|
||||
/// coincide.
|
||||
///
|
||||
/// # Ejemplos
|
||||
/// # Ejemplo
|
||||
///
|
||||
/// ```rust
|
||||
/// # use pagetop::prelude::*;
|
||||
/// let cx = Context::new(None)
|
||||
/// .with_param("usuario_id", 42_i32)
|
||||
/// .with_param("titulo", "Hola".to_string())
|
||||
/// .with_param("user_id", 42_i32)
|
||||
/// .with_param("title", "Hello".to_string())
|
||||
/// .with_param("flags", vec!["a", "b"]);
|
||||
/// ```
|
||||
#[builder_fn]
|
||||
|
|
@ -158,49 +158,43 @@ pub trait Contextual: LangId {
|
|||
/// Devuelve la plantilla configurada para renderizar el documento.
|
||||
fn template(&self) -> TemplateRef;
|
||||
|
||||
/// Recupera un parámetro como [`Option`], simplificando el acceso.
|
||||
/// Recupera una *referencia tipada* al parámetro solicitado.
|
||||
///
|
||||
/// A diferencia de [`get_param`](Context::get_param), que devuelve un [`Result`] con
|
||||
/// información detallada de error, este método devuelve `None` tanto si la clave no existe como
|
||||
/// si el valor guardado no coincide con el tipo solicitado.
|
||||
/// Devuelve:
|
||||
///
|
||||
/// Resulta útil en escenarios donde sólo interesa saber si el valor existe y es del tipo
|
||||
/// correcto, sin necesidad de diferenciar entre error de ausencia o de tipo.
|
||||
/// - `Ok(&T)` si la clave existe y el tipo coincide.
|
||||
/// - `Err(ContextError::ParamNotFound)` si la clave no existe.
|
||||
/// - `Err(ContextError::ParamTypeMismatch)` si la clave existe pero el tipo no coincide.
|
||||
///
|
||||
/// # Ejemplo
|
||||
///
|
||||
/// ```rust
|
||||
/// # use pagetop::prelude::*;
|
||||
/// let cx = Context::new(None).with_param("username", "Alice".to_string());
|
||||
/// let cx = Context::new(None)
|
||||
/// .with_param("user_id", 42_i32)
|
||||
/// .with_param("title", "Hello".to_string());
|
||||
///
|
||||
/// // Devuelve Some(&String) si existe y coincide el tipo.
|
||||
/// assert_eq!(cx.param::<String>("username").map(|s| s.as_str()), Some("Alice"));
|
||||
/// let id: i32 = *cx.param("user_id").unwrap();
|
||||
/// let title: &String = cx.param("title").unwrap();
|
||||
///
|
||||
/// // Devuelve None si no existe o si el tipo no coincide.
|
||||
/// assert!(cx.param::<i32>("username").is_none());
|
||||
/// assert!(cx.param::<String>("missing").is_none());
|
||||
///
|
||||
/// // Acceso con valor por defecto.
|
||||
/// let user = cx.param::<String>("missing")
|
||||
/// .cloned()
|
||||
/// .unwrap_or_else(|| "visitor".to_string());
|
||||
/// assert_eq!(user, "visitor");
|
||||
/// // Error de tipo:
|
||||
/// assert!(cx.param::<String>("user_id").is_err());
|
||||
/// ```
|
||||
fn param<T: 'static>(&self, key: &'static str) -> Option<&T>;
|
||||
fn param<T: 'static>(&self, key: &'static str) -> Result<&T, ContextError>;
|
||||
|
||||
/// Devuelve el parámetro clonado o el **valor por defecto del tipo** (`T::default()`).
|
||||
fn param_or_default<T: Default + Clone + 'static>(&self, key: &'static str) -> T {
|
||||
self.param::<T>(key).cloned().unwrap_or_default()
|
||||
fn param_or_default<T: Clone + Default + 'static>(&self, key: &'static str) -> T {
|
||||
self.param::<T>(key).ok().cloned().unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Devuelve el parámetro clonado o un **valor por defecto** si no existe.
|
||||
fn param_or<T: Clone + 'static>(&self, key: &'static str, default: T) -> T {
|
||||
self.param::<T>(key).cloned().unwrap_or(default)
|
||||
self.param::<T>(key).ok().cloned().unwrap_or(default)
|
||||
}
|
||||
|
||||
/// Devuelve el parámetro clonado o el **valor evaluado** por la función `f` si no existe.
|
||||
fn param_or_else<T: Clone + 'static, F: FnOnce() -> T>(&self, key: &'static str, f: F) -> T {
|
||||
self.param::<T>(key).cloned().unwrap_or_else(f)
|
||||
self.param::<T>(key).ok().cloned().unwrap_or_else(f)
|
||||
}
|
||||
|
||||
/// Devuelve el Favicon de los recursos del contexto.
|
||||
|
|
@ -214,27 +208,17 @@ pub trait Contextual: LangId {
|
|||
|
||||
// **< Contextual HELPERS >*********************************************************************
|
||||
|
||||
/// Devuelve el `id` proporcionado tal cual, o genera uno único para el tipo `T` si no se
|
||||
/// proporciona ninguno.
|
||||
///
|
||||
/// Si `id` es `None`, construye un identificador en la forma `<tipo>-<n>`, donde `<tipo>` es el
|
||||
/// nombre corto del tipo en minúsculas y `<n>` un contador incremental interno del contexto. Es
|
||||
/// útil para asignar identificadores HTML predecibles cuando el componente no recibe uno
|
||||
/// explícito.
|
||||
fn required_id<T>(&self, id: Option<String>) -> String;
|
||||
|
||||
/// Acumula un [`StatusMessage`] en el contexto para notificar al visitante.
|
||||
///
|
||||
/// Pueden generarse en cualquier punto del ciclo de una petición web (manejadores, renderizado,
|
||||
/// lógica de negocio, etc.) que tengan acceso al contexto, y mostrarlos luego, por ejemplo, en
|
||||
/// la página final devuelta al usuario.
|
||||
/// Elimina un parámetro del contexto. Devuelve `true` si la clave existía y se eliminó.
|
||||
///
|
||||
/// # Ejemplo
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// cx.push_message(MessageLevel::Warning, L10n::l("session-not-valid"));
|
||||
/// ```rust
|
||||
/// # use pagetop::prelude::*;
|
||||
/// let mut cx = Context::new(None).with_param("temp", 1u8);
|
||||
/// assert!(cx.remove_param("temp"));
|
||||
/// assert!(!cx.remove_param("temp")); // ya no existe
|
||||
/// ```
|
||||
fn push_message(&mut self, level: MessageLevel, text: L10n);
|
||||
fn remove_param(&mut self, key: &'static str) -> bool;
|
||||
}
|
||||
|
||||
/// Implementa un **contexto de renderizado** para un documento HTML.
|
||||
|
|
@ -264,7 +248,7 @@ pub trait Contextual: LangId {
|
|||
/// // Añade un script JavaScript.
|
||||
/// .with_assets(AssetsOp::AddJavaScript(JavaScript::defer("/js/main.js")))
|
||||
/// // Añade un parámetro dinámico al contexto.
|
||||
/// .with_param("usuario_id", 42)
|
||||
/// .with_param("user_id", 42)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
|
|
@ -272,18 +256,22 @@ pub trait Contextual: LangId {
|
|||
///
|
||||
/// ```rust
|
||||
/// # use pagetop::prelude::*;
|
||||
/// # #[derive(AutoDefault, Clone, Debug)]
|
||||
/// # struct Menu;
|
||||
/// # impl Component for Menu {
|
||||
/// # fn new() -> Self { Self::default() }
|
||||
/// # }
|
||||
/// fn use_context(cx: &mut Context) {
|
||||
/// // Recupera el tema seleccionado.
|
||||
/// let active_theme = cx.theme();
|
||||
/// assert_eq!(active_theme.short_name(), "aliner");
|
||||
///
|
||||
/// // Recupera el parámetro a su tipo original.
|
||||
/// let id: i32 = *cx.get_param::<i32>("usuario_id").unwrap();
|
||||
/// let id: i32 = *cx.param::<i32>("user_id").unwrap();
|
||||
/// assert_eq!(id, 42);
|
||||
///
|
||||
/// // Genera un identificador para un componente de tipo `Menu`.
|
||||
/// struct Menu;
|
||||
/// let unique_id = cx.required_id::<Menu>(None);
|
||||
/// let unique_id = cx.required_id::<Menu>(None, 1);
|
||||
/// assert_eq!(unique_id, "menu-1"); // Si es el primero generado.
|
||||
/// }
|
||||
/// ```
|
||||
|
|
@ -298,7 +286,7 @@ pub struct Context {
|
|||
javascripts: Assets<JavaScript>, // Scripts JavaScript.
|
||||
regions : ChildrenInRegions, // Regiones de componentes para renderizar.
|
||||
params : HashMap<&'static str, (Box<dyn Any>, &'static str)>, // Parámetros en ejecución.
|
||||
id_counter : Cell<usize>, // Cell permite incrementarlo desde &self en required_id().
|
||||
id_counter : Cell<usize>, // Cell permite incrementar desde &self en required_id().
|
||||
messages : Vec<StatusMessage>, // Mensajes de usuario acumulados.
|
||||
}
|
||||
|
||||
|
|
@ -366,90 +354,6 @@ impl Context {
|
|||
.render(self)
|
||||
}
|
||||
|
||||
// **< Context PARAMS >*************************************************************************
|
||||
|
||||
/// Recupera una *referencia tipada* al parámetro solicitado.
|
||||
///
|
||||
/// Devuelve:
|
||||
///
|
||||
/// - `Ok(&T)` si la clave existe y el tipo coincide.
|
||||
/// - `Err(ContextError::ParamNotFound)` si la clave no existe.
|
||||
/// - `Err(ContextError::ParamTypeMismatch)` si la clave existe pero el tipo no coincide.
|
||||
///
|
||||
/// # Ejemplos
|
||||
///
|
||||
/// ```rust
|
||||
/// # use pagetop::prelude::*;
|
||||
/// let cx = Context::new(None)
|
||||
/// .with_param("usuario_id", 42_i32)
|
||||
/// .with_param("titulo", "Hola".to_string());
|
||||
///
|
||||
/// let id: &i32 = cx.get_param("usuario_id").unwrap();
|
||||
/// let titulo: &String = cx.get_param("titulo").unwrap();
|
||||
///
|
||||
/// // Error de tipo:
|
||||
/// assert!(cx.get_param::<String>("usuario_id").is_err());
|
||||
/// ```
|
||||
pub fn get_param<T: 'static>(&self, key: &'static str) -> Result<&T, ContextError> {
|
||||
let (any, type_name) = self.params.get(key).ok_or(ContextError::ParamNotFound)?;
|
||||
any.downcast_ref::<T>()
|
||||
.ok_or_else(|| ContextError::ParamTypeMismatch {
|
||||
key,
|
||||
expected: TypeInfo::FullName.of::<T>(),
|
||||
saved: type_name,
|
||||
})
|
||||
}
|
||||
|
||||
/// Recupera el parámetro solicitado y lo elimina del contexto.
|
||||
///
|
||||
/// Devuelve:
|
||||
///
|
||||
/// - `Ok(T)` si la clave existía y el tipo coincide.
|
||||
/// - `Err(ContextError::ParamNotFound)` si la clave no existe.
|
||||
/// - `Err(ContextError::ParamTypeMismatch)` si el tipo no coincide.
|
||||
///
|
||||
/// # Ejemplos
|
||||
///
|
||||
/// ```rust
|
||||
/// # use pagetop::prelude::*;
|
||||
/// let mut cx = Context::new(None)
|
||||
/// .with_param("contador", 7_i32)
|
||||
/// .with_param("titulo", "Hola".to_string());
|
||||
///
|
||||
/// let n: i32 = cx.take_param("contador").unwrap();
|
||||
/// assert!(cx.get_param::<i32>("contador").is_err()); // ya no está
|
||||
///
|
||||
/// // Error de tipo:
|
||||
/// assert!(cx.take_param::<i32>("titulo").is_err());
|
||||
/// ```
|
||||
pub fn take_param<T: 'static>(&mut self, key: &'static str) -> Result<T, ContextError> {
|
||||
let (boxed, saved) = self.params.remove(key).ok_or(ContextError::ParamNotFound)?;
|
||||
boxed
|
||||
.downcast::<T>()
|
||||
.map(|b| *b)
|
||||
.map_err(|_| ContextError::ParamTypeMismatch {
|
||||
key,
|
||||
expected: TypeInfo::FullName.of::<T>(),
|
||||
saved,
|
||||
})
|
||||
}
|
||||
|
||||
/// Elimina un parámetro del contexto. Devuelve `true` si la clave existía y se eliminó.
|
||||
///
|
||||
/// Devuelve `false` en caso contrario. Usar cuando sólo interesa borrar la entrada.
|
||||
///
|
||||
/// # Ejemplos
|
||||
///
|
||||
/// ```rust
|
||||
/// # use pagetop::prelude::*;
|
||||
/// let mut cx = Context::new(None).with_param("temp", 1u8);
|
||||
/// assert!(cx.remove_param("temp"));
|
||||
/// assert!(!cx.remove_param("temp")); // ya no existe
|
||||
/// ```
|
||||
pub fn remove_param(&mut self, key: &'static str) -> bool {
|
||||
self.params.remove(key).is_some()
|
||||
}
|
||||
|
||||
// **< Context HELPERS >************************************************************************
|
||||
|
||||
/// Construye una ruta aplicada al contexto actual.
|
||||
|
|
@ -470,6 +374,51 @@ impl Context {
|
|||
route
|
||||
}
|
||||
|
||||
/// Garantiza un identificador único para un componente `C`, generándolo si no se proporciona
|
||||
/// ninguno.
|
||||
///
|
||||
/// Si `id` es `None`, crea un identificador usando los últimos segmentos del *path* completo
|
||||
/// del tipo `C`, separados por `-` y en minúsculas, seguidos de un contador incremental interno
|
||||
/// del contexto. Por ejemplo, para un componente `MyApp::ui::Menu` con `parts = 2` podría
|
||||
/// devolver un identificador como `ui-menu-1` si ha sido el primero en generarse.
|
||||
///
|
||||
/// Con `parts = 1` se usa el nombre corto del tipo. Si `parts` es `0` o supera el número de
|
||||
/// segmentos del *path*, entonces se usará el *path* completo.
|
||||
///
|
||||
/// Es útil para asignar identificadores HTML predecibles cuando el componente no recibe uno
|
||||
/// explícito.
|
||||
pub fn required_id<C: Component>(&self, id: Option<String>, parts: usize) -> String {
|
||||
if let Some(id) = id {
|
||||
return id;
|
||||
}
|
||||
let segments: Vec<&str> = TypeInfo::FullName.of::<C>().split("::").collect();
|
||||
let parts = if parts == 0 || parts >= segments.len() {
|
||||
segments.len()
|
||||
} else {
|
||||
parts
|
||||
};
|
||||
self.id_counter.set(self.id_counter.get() + 1);
|
||||
let prefix = segments[segments.len() - parts..].join("-").to_lowercase();
|
||||
util::join!(prefix, "-", self.id_counter.get().to_string())
|
||||
}
|
||||
|
||||
/// Acumula un [`StatusMessage`] en el contexto para notificar al visitante.
|
||||
///
|
||||
/// Pueden generarse en cualquier punto del ciclo de una petición web (manejadores, renderizado,
|
||||
/// lógica de negocio, etc.) que tengan acceso al contexto, y mostrarlos luego, por ejemplo, en
|
||||
/// la página final devuelta al usuario.
|
||||
///
|
||||
/// # Ejemplo
|
||||
///
|
||||
/// ```rust
|
||||
/// # use pagetop::prelude::*;
|
||||
/// # let mut cx = Context::new(None);
|
||||
/// cx.push_message(MessageLevel::Warning, L10n::n("Session is not valid"));
|
||||
/// ```
|
||||
pub fn push_message(&mut self, level: MessageLevel, text: L10n) {
|
||||
self.messages.push(StatusMessage::new(level, text));
|
||||
}
|
||||
|
||||
/// Devuelve todos los mensajes de usuario acumulados.
|
||||
pub fn messages(&self) -> &[StatusMessage] {
|
||||
&self.messages
|
||||
|
|
@ -589,8 +538,14 @@ impl Contextual for Context {
|
|||
self.template
|
||||
}
|
||||
|
||||
fn param<T: 'static>(&self, key: &'static str) -> Option<&T> {
|
||||
self.get_param::<T>(key).ok()
|
||||
fn param<T: 'static>(&self, key: &'static str) -> Result<&T, ContextError> {
|
||||
let (any, type_name) = self.params.get(key).ok_or(ContextError::ParamNotFound)?;
|
||||
any.downcast_ref::<T>()
|
||||
.ok_or_else(|| ContextError::ParamTypeMismatch {
|
||||
key,
|
||||
expected: TypeInfo::FullName.of::<T>(),
|
||||
saved: type_name,
|
||||
})
|
||||
}
|
||||
|
||||
fn favicon(&self) -> Option<&Favicon> {
|
||||
|
|
@ -607,26 +562,7 @@ impl Contextual for Context {
|
|||
|
||||
// **< Contextual HELPERS >*********************************************************************
|
||||
|
||||
fn required_id<T>(&self, id: Option<String>) -> String {
|
||||
if let Some(id) = id {
|
||||
id
|
||||
} else {
|
||||
let prefix = TypeInfo::ShortName
|
||||
.of::<T>()
|
||||
.trim()
|
||||
.replace(' ', "_")
|
||||
.to_lowercase();
|
||||
let prefix = if prefix.is_empty() {
|
||||
"prefix".to_string()
|
||||
} else {
|
||||
prefix
|
||||
};
|
||||
self.id_counter.set(self.id_counter.get() + 1);
|
||||
util::join!(prefix, "-", self.id_counter.get().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn push_message(&mut self, level: MessageLevel, text: L10n) {
|
||||
self.messages.push(StatusMessage::new(level, text));
|
||||
fn remove_param(&mut self, key: &'static str) -> bool {
|
||||
self.params.remove(key).is_some()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ pub use error::ErrorPage;
|
|||
pub use actix_web::Result as ResultPage;
|
||||
|
||||
use crate::base::action;
|
||||
use crate::core::component::{AssetsOp, ChildOp, Context, Contextual};
|
||||
use crate::core::component::{AssetsOp, ChildOp, Context, ContextError, Contextual};
|
||||
use crate::core::theme::{DefaultRegion, Region, RegionRef, TemplateRef, ThemeRef};
|
||||
use crate::html::{html, Markup, DOCTYPE};
|
||||
use crate::html::{Assets, Favicon, JavaScript, StyleSheet};
|
||||
|
|
@ -349,7 +349,7 @@ impl Contextual for Page {
|
|||
self.context.template()
|
||||
}
|
||||
|
||||
fn param<T: 'static>(&self, key: &'static str) -> Option<&T> {
|
||||
fn param<T: 'static>(&self, key: &'static str) -> Result<&T, ContextError> {
|
||||
self.context.param(key)
|
||||
}
|
||||
|
||||
|
|
@ -367,11 +367,7 @@ impl Contextual for Page {
|
|||
|
||||
// **< Contextual HELPERS >*********************************************************************
|
||||
|
||||
fn required_id<T>(&self, id: Option<String>) -> String {
|
||||
self.context.required_id::<T>(id)
|
||||
}
|
||||
|
||||
fn push_message(&mut self, level: crate::prelude::MessageLevel, text: L10n) {
|
||||
self.context.push_message(level, text);
|
||||
fn remove_param(&mut self, key: &'static str) -> bool {
|
||||
self.context.remove_param(key)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue