yewstack / yew

Rust / Wasm framework for creating reliable and efficient web applications

Home Page:https://yew.rs

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

checked attribute is lost

nirvdrum opened this issue · comments

Problem

I'm seeing a problem where the checked attribute is dropped when using a web component from the Shoelace framework. I'm not entirely sure if the issue is with Yew or with Shoelace, but it's demonstrably not a problem in Shoelace when writing manual HTML.

Steps To Reproduce
Steps to reproduce the behavior:

  1. Include Shoelace in your Yew project
  2. Write a new Yew component
  3. Return a web component with the checked attribute set (e.g., html! { <sl-checkbox checked={true}>{ "my option" }</sl-checkbox> })

Expected behavior

I'd expect to see the checked attribute set on the component. Instead, the checkbox is not clicked and the DOM element does not have the checked attribute set.

I do not run into this problem using a built-in HTML element, such as <input type="checkbox" checked=true />

Environment:

  • Yew version: 0.20
  • Rust version: 1.68.0
  • Target, if relevant: wasm32-unknown-unknown
  • Build tool, if relevant: trunk
  • OS, if relevant: macOS
  • Browser and version, if relevant: It doesn't appear to be bound to any particular browser

Questionnaire

  • I'm interested in fixing this myself but don't know where to start
  • I would like to fix and I have a solution
  • I don't have time to fix this right now, but maybe later

Digging in a bit more, the checked state works if I use Html::from_html_unchecked to build up the <sl-checkbox> web component.

I'm still getting my feet wet with Yew internals, but I think the problem is the checked attribute is ignored for anything other than an <input> tag:

/// Sets `checked` property of an
/// [InputElement](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
/// (Does not affect the value of the node's attribute).
pub fn set_checked(&mut self, value: bool) {
if let VTagInner::Input(f) = &mut self.inner {
f.checked = Some(value);
}
}

Here, I need to set it as an attribute on the web component. It's ugly, but if I dig into the VNode returned from html! and get at the underlying VTag, I can call add_attribute("checked", true) and get what I need. It's more annoying than creating from a string, but is safer since I'll still get HTML entity escaping.

I think it's a bug in the html! macro. Basically, the attribute gets specially handled during parsing but only ends up being used if the element is an <input>. I suppose that makes sense as long as one uses only the standard HTML elements, but breaks with custom elements that can have an checked attribute.

let checked = props.pop("checked");
here it is being separated from the other attributes.

let checked = checked
.as_ref()
.map(|attr| {
let value = &attr.value;
quote! { ::std::option::Option::Some( #value ) }
})
.unwrap_or(quote! { ::std::option::Option::None });
here checked ends up only being used with <input>.

I think it would make sense to fix this in the parsing and handle it specially only when the element is actually known to be an <input>.