leptos-rs / leptos

Build fast web applications with Rust.
https://leptos.dev
MIT License
15.31k stars 599 forks source link

`HtmlElement::attrs` and `DynAttrs::dyn_attrs` Accept `&'static str` Instead of `Oco<'static, str>` #2594

Open Defernus opened 1 month ago

Defernus commented 1 month ago

Describe the bug The leptos_dom::html::HtmlElement::attrs method does not accept Vec<(Oco<'static, str>, Attribute)>.

However, it will accept Vec<(&'static str, Attribute)> and will convert name to Oco<'static, str> in the HtmlElement::attr method.

The same issue occurs with leptos::DynAttrs::dyn_attrs, which will only accept Vec<(&'static str, Attribute)>.

Leptos Dependencies

leptos = { version = "0.6", features = ["csr", "nightly"] }

To Reproduce

use leptos::*;

#[component]
pub fn MyComponent(
    #[prop(attrs, optional)] attrs: Vec<(Oco<'static, str>, Attribute)>,
) -> impl IntoView {
    html::span().attrs(attrs)
}

Expected behavior

The method should accept Vec<(Oco<'static, str>, Attribute)> as a type for dynamic attributes.

Additional context

I am working on Storybook integration for Leptos and parsing props for components from JsValue. Currently, it is impossible to use regular arguments, so I have to do something like this:

use leptos::*;
use wasm_bindgen::prelude::*;

// we have to create wrapper type for attributes
#[derive(Default)]
pub struct AttributeSpread(pub Vec<(Oco<'static, str>, Attribute)>);

impl From<Vec<(&'static str, leptos::Attribute)>> for AttributeSpread {
    fn from(value: Vec<(&'static str, leptos::Attribute)>) -> Self {
        Self(
            value
                .into_iter()
                .map(|(name, value)| (Oco::Borrowed(name), value))
                .collect(),
        )
    }
}

#[component]
pub fn MyComponent(#[prop(attrs, optional)] attrs: AttributeSpread) -> impl IntoView {
    let mut span = html::span();

    // have to add each attr manually
    for (name, value) in attrs.0 {
        span = span.attr(name, value);
    }

    span
}
gbj commented 1 month ago

I don't think it's possible to change these function signatures in a way that isn't technically a breaking change, although I may be wrong. I do know that they will change significantly in the next release as part of the wider renderer rewrite, so I will mark this issue as 0.7 to make sure there's a decent solution then.

0.7 does support spreading arbitrary attributes of any kind onto any component, which seems to me like it should help.

Defernus commented 1 month ago

Also, I don't see any reason why the Binding::Attribute name is &'static str instead of Oco<'static, str>. Because of this, I can't do the following:

use leptos::*;

#[component]
pub fn MyComponent(
    #[prop(attrs, optional)] attrs: Vec<(Oco<'static, str>, Attribute)>,
) -> impl IntoView {
    view! { <span {..attrs}></span> }
}