Setting default value for web component attributes
Say I have a component with optional fields:
#[component]
fn input<'a>(name: &'a str, value: Option<&'a str>, error: Option<&'a str>, max_length: u8, ...) -> impl Renderable {
...
}
It's tedious to enter values of all fields (rsx syntax) each time I use this component.
<Input name="email" value=None error=None max_length=32 ...>
I would like to propose a way to set default values for each attribute. The syntax could look like this:
#[component]
fn input<'a>(
name: &'a str,
#[default] value: Option<&'a str>,
#[default] error: Option<&'a str>,
#[default(32)] max_length: u8,
...
) -> impl Renderable {
...
}
#[default] uses the Default trait to get the value. You can also specify a value using #[default(value)].
Then you can just use Input component without repetition. You can only specify the attributes you want to override.
<Input name="email" value=(Some("John"))>
What do you think?
@bshankar thank you for the idea!
an oversight on my end is that it is missing from the docs, but components actually do support a Default syntax.
rsx! { <Input value=(Some("John")) .. /> }
will use ..Default::default() to fill the rest of the struct. As for the implementation, you can manually impl Default for Input under the component function. Would that be an acceptable solution?
I don't mind the syntax of using .. or implementing Default for the struct but this automatically gives a default value for all attributes - making all of them optional. I prefer some attributes / props to be mandatory and some optional.
(Sorry my example was bad. I updated it)
Ah I see. Unfortunately there is no real way to communicate to the struct from the declaration to the component usage which fields are required and which aren't. How would you suggest this is resolved?
IMO the best way to do this currently would be to just create a couple constructors like such:
#[component]
fn input<'a>(
name: &'a str,
value: Option<&'a str>,
error: Option<&'a str>,
max_length: u8,
) -> impl Renderable {
// ...
}
impl<'a> Input<'a> {
fn new(name: &'a str) -> Self {
Self {
name,
// fill rest with your defaults here
}
}
fn with_value(mut self, value: &'a str) -> Self {
self.value = Some(value)
self
}
// ...
and forgo the entire "component" syntax, instead just desugaring it into what the component syntax would eventually become:
rsx! {
<div>
(Input::new("input name").with_value(...))
</div>
}
You could even use a crate like bon to simplify the builder generating, and instead of #[component] use #[derive(Renderable)]
Yea that's a good way to do this currently. I'm thinking of another usage pattern too. We can combine the optional fields into it's own type which implements Default.
struct OptionalProps<'a> {
value: Option<&'a str>,
error: Option<&'a str>,
max_length: u8,
}
impl Default for OptionalProps {
fn default() -> Self {
Self {
max_length: 32,
..Default::default()
}
}
}
#[component]
fn input<'a>(
name: &'a str,
optional_props: &OptionalProps,
) -> impl Renderable {
// ...
}
Then we don't need to use the desugared syntax or bon.
rsx! {
<Input
name="email"
optional_props=(OptionalProps {
value: Some("[email protected]"),
..Default::default()
})
/>
}
BTW you can declare default values in a struct in nightly Rust. It was planned for Rust 1.85 IIRC but ended up being delayed. https://github.com/rust-lang/rust/issues/132162
Maybe we can have a more elegant solution then?
Yes, that RFC would definitely simplify this. Your idea would also work great, it's basically the current best way to have a couple default fields. This wouldn't require any changes to hypertext either.
closing this for now, it can be revisited when https://github.com/rust-lang/rust/issues/132162 is added to stable rust.