diff --git a/docs/book/src/metadata.md b/docs/book/src/metadata.md index 5eacd1c6c2..665157da5a 100644 --- a/docs/book/src/metadata.md +++ b/docs/book/src/metadata.md @@ -30,13 +30,13 @@ There’s a very simple way to determine whether you should use a capital-S ``](https://docs.rs/leptos_meta/latest/leptos_meta/fn.Html.html) lets you set the `lang` and `dir` on your `` tag from your application code. `` and [``](https://docs.rs/leptos_meta/latest/leptos_meta/fn.Html.html) both have `class` props that let you set their respective `class` attributes, which is sometimes needed by CSS frameworks for styling. -`` and `` both also have `attributes` props which can be used to set any number of additional attributes on them via the [`AdditionalAttributes`](https://docs.rs/leptos/latest/leptos/struct.AdditionalAttributes.html) type: +`` and `` both also have `attributes` props which can be used to set any number of additional attributes on them via the `attr:` syntax: ```rust ``` diff --git a/leptos/src/additional_attributes.rs b/leptos/src/additional_attributes.rs index 4be7abe63a..ba39621613 100644 --- a/leptos/src/additional_attributes.rs +++ b/leptos/src/additional_attributes.rs @@ -1,3 +1,5 @@ +#![allow(deprecated)] + use crate::TextProp; use std::rc::Rc; @@ -5,6 +7,9 @@ use std::rc::Rc; /// each of which may or may not be reactive. #[derive(Clone)] #[repr(transparent)] +#[deprecated = "Most uses of `AdditionalAttributes` can be replaced with `#[prop(attrs)]` \ +and the `attr:` syntax. If you have a use case that still requires `AdditionalAttributes`, please \ +open a GitHub issue here and share it: https://github.com/leptos-rs/leptos"] pub struct AdditionalAttributes(pub(crate) Rc<[(String, TextProp)]>); impl From for AdditionalAttributes diff --git a/meta/src/body.rs b/meta/src/body.rs index 157419f522..e5eb078a65 100644 --- a/meta/src/body.rs +++ b/meta/src/body.rs @@ -1,6 +1,8 @@ use cfg_if::cfg_if; use leptos::*; #[cfg(feature = "ssr")] +use std::collections::HashMap; +#[cfg(feature = "ssr")] use std::{cell::RefCell, rc::Rc}; /// Contains the current metadata for the document's ``. @@ -9,7 +11,7 @@ pub struct BodyContext { #[cfg(feature = "ssr")] class: Rc>>, #[cfg(feature = "ssr")] - attributes: Rc>>>, + attributes: Rc>>, } impl BodyContext { @@ -22,20 +24,23 @@ impl BodyContext { leptos::leptos_dom::ssr::escape_attr(&val.get()) ) }); - let attributes = self.attributes.borrow().as_ref().map(|val| { - val.with(|val| { - val.into_iter() - .map(|(n, v)| { + let attributes = self.attributes.borrow(); + let attributes = (!attributes.is_empty()).then(|| { + attributes + .iter() + .filter_map(|(n, v)| { + v.as_nameless_value_string().map(|v| { format!( "{}=\"{}\"", n, - leptos::leptos_dom::ssr::escape_attr(&v.get()) + leptos::leptos_dom::ssr::escape_attr(&v) ) }) - .collect::>() - .join(" ") - }) + }) + .collect::>() + .join(" ") }); + let mut val = [class, attributes] .into_iter() .flatten() @@ -77,7 +82,7 @@ impl std::fmt::Debug for BodyContext { /// /// view! { ///
-/// +/// ///
/// } /// } @@ -88,11 +93,13 @@ pub fn Body( #[prop(optional, into)] class: Option, /// Arbitrary attributes to add to the `` - #[prop(optional, into)] - attributes: Option>, + #[prop(attrs)] + attributes: Vec<(&'static str, Attribute)>, ) -> impl IntoView { cfg_if! { - if #[cfg(any(feature = "csr", feature = "hydrate"))] { + if #[cfg(all(target_arch = "wasm32", any(feature = "csr", feature = "hydrate")))] { + use wasm_bindgen::JsCast; + let el = document().body().expect("there to be a element"); if let Some(class) = class { @@ -105,24 +112,15 @@ pub fn Body( }); } - if let Some(attributes) = attributes { - let attributes = attributes.get(); - for (attr_name, attr_value) in attributes.into_iter() { - let el = el.clone(); - let attr_name = attr_name.to_owned(); - let attr_value = attr_value.to_owned(); - create_render_effect(move |_|{ - let value = attr_value.get(); - _ = el.set_attribute(&attr_name, &value); - }); - } + for (name, value) in attributes { + leptos::leptos_dom::attribute_helper(el.unchecked_ref(), name.into(), value); } } else if #[cfg(feature = "ssr")] { let meta = crate::use_head(); *meta.body.class.borrow_mut() = class; - *meta.body.attributes.borrow_mut() = attributes; + meta.body.attributes.borrow_mut().extend(attributes); } else { - _ = class; + _ = class; _ = attributes; #[cfg(debug_assertions)] diff --git a/meta/src/html.rs b/meta/src/html.rs index ec91373afb..0cca4e3f0e 100644 --- a/meta/src/html.rs +++ b/meta/src/html.rs @@ -1,6 +1,8 @@ use cfg_if::cfg_if; use leptos::*; #[cfg(feature = "ssr")] +use std::collections::HashMap; +#[cfg(feature = "ssr")] use std::{cell::RefCell, rc::Rc}; /// Contains the current metadata for the document's ``. @@ -13,7 +15,7 @@ pub struct HtmlContext { #[cfg(feature = "ssr")] class: Rc>>, #[cfg(feature = "ssr")] - attributes: Rc>>>, + attributes: Rc>>, } impl HtmlContext { @@ -38,19 +40,21 @@ impl HtmlContext { leptos::leptos_dom::ssr::escape_attr(&val.get()) ) }); - let attributes = self.attributes.borrow().as_ref().map(|val| { - val.with(|val| { - val.into_iter() - .map(|(n, v)| { + let attributes = self.attributes.borrow(); + let attributes = (!attributes.is_empty()).then(|| { + attributes + .iter() + .filter_map(|(n, v)| { + v.as_nameless_value_string().map(|v| { format!( "{}=\"{}\"", n, - leptos::leptos_dom::ssr::escape_attr(&v.get()) + leptos::leptos_dom::ssr::escape_attr(&v) ) }) - .collect::>() - .join(" ") - }) + }) + .collect::>() + .join(" ") }); let mut val = [lang, dir, class, attributes] .into_iter() @@ -88,8 +92,8 @@ impl std::fmt::Debug for HtmlContext { /// /// /// } @@ -107,11 +111,13 @@ pub fn Html( #[prop(optional, into)] class: Option, /// Arbitrary attributes to add to the `` - #[prop(optional, into)] - attributes: Option>, + #[prop(attrs)] + attributes: Vec<(&'static str, Attribute)>, ) -> impl IntoView { cfg_if! { - if #[cfg(any(feature = "csr", feature = "hydrate"))] { + if #[cfg(all(target_arch = "wasm32", any(feature = "csr", feature = "hydrate")))] { + use wasm_bindgen::JsCast; + let el = document().document_element().expect("there to be a element"); if let Some(lang) = lang { @@ -138,24 +144,15 @@ pub fn Html( }); } - if let Some(attributes) = attributes { - let attributes = attributes.get(); - for (attr_name, attr_value) in attributes.into_iter() { - let el = el.clone(); - let attr_name = attr_name.to_owned(); - let attr_value = attr_value.to_owned(); - create_render_effect(move |_|{ - let value = attr_value.get(); - _ = el.set_attribute(&attr_name, &value); - }); - } + for (name, value) in attributes { + leptos::leptos_dom::attribute_helper(el.unchecked_ref(), name.into(), value); } } else if #[cfg(feature = "ssr")] { let meta = crate::use_head(); *meta.html.lang.borrow_mut() = lang; *meta.html.dir.borrow_mut() = dir; *meta.html.class.borrow_mut() = class; - *meta.html.attributes.borrow_mut() = attributes; + meta.html.attributes.borrow_mut().extend(attributes); } else { _ = lang; _ = dir; diff --git a/router/src/components/form.rs b/router/src/components/form.rs index 48891004bf..2c885df80f 100644 --- a/router/src/components/form.rs +++ b/router/src/components/form.rs @@ -56,9 +56,10 @@ pub fn Form( /// Sets whether the page should be scrolled to the top when the form is submitted. #[prop(optional)] noscroll: bool, - /// Arbitrary attributes to add to the `
` - #[prop(optional, into)] - attributes: Option>, + /// Arbitrary attributes to add to the ``. Attributes can be added with the + /// `attr:` syntax in the `view` macro. + #[prop(attrs)] + attributes: Vec<(&'static str, Attribute)>, /// Component children; should include the HTML of the form elements. children: Children, ) -> impl IntoView @@ -78,7 +79,7 @@ where children: Children, node_ref: Option>, noscroll: bool, - attributes: Option>, + attributes: Vec<(&'static str, Attribute)>, ) -> HtmlElement { let action_version = version; let on_submit = { @@ -276,13 +277,8 @@ where if let Some(node_ref) = node_ref { form = form.node_ref(node_ref) }; - if let Some(attributes) = attributes { - let attributes = attributes.get(); - for (attr_name, attr_value) in attributes.into_iter() { - let attr_name = attr_name.to_owned(); - let attr_value = attr_value.to_owned(); - form = form.attr(attr_name, move || attr_value.get()); - } + for (attr_name, attr_value) in attributes { + form = form.attr(attr_name, attr_value); } form } @@ -352,7 +348,7 @@ pub fn ActionForm( noscroll: bool, /// Arbitrary attributes to add to the `` #[prop(optional, into)] - attributes: Option>, + attributes: Vec<(&'static str, Attribute)>, /// Component children; should include the HTML of the form elements. children: Children, ) -> impl IntoView @@ -539,7 +535,7 @@ pub fn MultiActionForm( node_ref: Option>, /// Arbitrary attributes to add to the `` #[prop(optional, into)] - attributes: Option>, + attributes: Vec<(&'static str, Attribute)>, /// Component children; should include the HTML of the form elements. children: Children, ) -> impl IntoView @@ -591,13 +587,8 @@ where if let Some(node_ref) = node_ref { form = form.node_ref(node_ref) }; - if let Some(attributes) = attributes { - let attributes = attributes.get(); - for (attr_name, attr_value) in attributes.into_iter() { - let attr_name = attr_name.to_owned(); - let attr_value = attr_value.to_owned(); - form = form.attr(attr_name, move || attr_value.get()); - } + for (attr_name, attr_value) in attributes { + form = form.attr(attr_name, attr_value); } form }