diff --git a/packages/yew-macro/src/html_tree/html_element.rs b/packages/yew-macro/src/html_tree/html_element.rs
index 6e038d7041a..31545029995 100644
--- a/packages/yew-macro/src/html_tree/html_element.rs
+++ b/packages/yew-macro/src/html_tree/html_element.rs
@@ -8,7 +8,7 @@ use syn::spanned::Spanned;
use syn::{Block, Expr, Ident, Lit, LitStr, Token};
use super::{HtmlChildrenTree, HtmlDashedName, TagTokens};
-use crate::props::{ClassesForm, ElementProps, Prop};
+use crate::props::{ClassesForm, ElementProps, Prop, PropDirective};
use crate::stringify::{Stringify, Value};
use crate::{non_capitalized_ascii, Peek, PeekValue};
@@ -139,13 +139,13 @@ impl ToTokens for HtmlElement {
|Prop {
label,
value,
- is_forced_attribute,
+ directive,
..
}| {
(
label.to_lit_str(),
value.optimize_literals_tagged(),
- *is_forced_attribute,
+ *directive,
)
},
);
@@ -153,7 +153,7 @@ impl ToTokens for HtmlElement {
|Prop {
label,
value,
- is_forced_attribute,
+ directive,
..
}| {
let key = label.to_lit_str();
@@ -183,7 +183,7 @@ impl ToTokens for HtmlElement {
},
),
},
- *is_forced_attribute,
+ *directive,
))
},
);
@@ -215,7 +215,7 @@ impl ToTokens for HtmlElement {
__yew_classes
}
}),
- false,
+ None,
))
}
ClassesForm::Single(classes) => {
@@ -227,7 +227,7 @@ impl ToTokens for HtmlElement {
Some((
LitStr::new("class", lit.span()),
Value::Static(quote! { #lit }),
- false,
+ None,
))
}
}
@@ -237,26 +237,29 @@ impl ToTokens for HtmlElement {
Value::Dynamic(quote! {
::std::convert::Into::<::yew::html::Classes>::into(#classes)
}),
- false,
+ None,
))
}
}
}
});
+ fn apply_as(directive: Option<&PropDirective>) -> TokenStream {
+ match directive {
+ Some(PropDirective::ApplyAsProperty(token)) => quote_spanned!(token.span()=> ::yew::virtual_dom::ApplyAttributeAs::Property),
+ None => quote!(::yew::virtual_dom::ApplyAttributeAs::Attribute),
+ }
+ }
+
/// Try to turn attribute list into a `::yew::virtual_dom::Attributes::Static`
- fn try_into_static(src: &[(LitStr, Value, bool)]) -> Option {
+ fn try_into_static(src: &[(LitStr, Value, Option)]) -> Option {
let mut kv = Vec::with_capacity(src.len());
- for (k, v, is_forced_attribute) in src.iter() {
+ for (k, v, directive) in src.iter() {
let v = match v {
Value::Static(v) => quote! { #v },
Value::Dynamic(_) => return None,
};
- let apply_as = if *is_forced_attribute {
- quote! { ::yew::virtual_dom::ApplyAttributeAs::Attribute }
- } else {
- quote! { ::yew::virtual_dom::ApplyAttributeAs::Property }
- };
+ let apply_as = apply_as(directive.as_ref());
kv.push(quote! { ( #k, #v, #apply_as ) });
}
@@ -266,15 +269,11 @@ impl ToTokens for HtmlElement {
let attrs = normal_attrs
.chain(boolean_attrs)
.chain(class_attr)
- .collect::>();
+ .collect::)>>();
try_into_static(&attrs).unwrap_or_else(|| {
let keys = attrs.iter().map(|(k, ..)| quote! { #k });
- let values = attrs.iter().map(|(_, v, is_forced_attribute)| {
- let apply_as = if *is_forced_attribute {
- quote! { ::yew::virtual_dom::ApplyAttributeAs::Attribute }
- } else {
- quote! { ::yew::virtual_dom::ApplyAttributeAs::Property }
- };
+ let values = attrs.iter().map(|(_, v, directive)| {
+ let apply_as = apply_as(directive.as_ref());
let value = wrap_attr_value(v);
quote! { ::std::option::Option::map(#value, |it| (it, #apply_as)) }
});
diff --git a/packages/yew-macro/src/props/prop.rs b/packages/yew-macro/src/props/prop.rs
index 5d840032ec2..7bb3c75b0ea 100644
--- a/packages/yew-macro/src/props/prop.rs
+++ b/packages/yew-macro/src/props/prop.rs
@@ -13,19 +13,24 @@ use super::CHILDREN_LABEL;
use crate::html_tree::HtmlDashedName;
use crate::stringify::Stringify;
+#[derive(Copy, Clone)]
+pub enum PropDirective {
+ ApplyAsProperty(Token![~]),
+}
+
pub struct Prop {
- pub is_forced_attribute: bool,
+ pub directive: Option,
pub label: HtmlDashedName,
/// Punctuation between `label` and `value`.
pub value: Expr,
}
impl Parse for Prop {
fn parse(input: ParseStream) -> syn::Result {
- let at = input.parse::().map(|_| true).unwrap_or(false);
+ let directive = input.parse::().map(|parsed| PropDirective::ApplyAsProperty(parsed)).ok();
if input.peek(Brace) {
- Self::parse_shorthand_prop_assignment(input, at)
+ Self::parse_shorthand_prop_assignment(input, directive)
} else {
- Self::parse_prop_assignment(input, at)
+ Self::parse_prop_assignment(input, directive)
}
}
}
@@ -37,7 +42,7 @@ impl Prop {
/// an ambiguity in the syntax
fn parse_shorthand_prop_assignment(
input: ParseStream,
- is_forced_attribute: bool,
+ directive: Option,
) -> syn::Result {
let value;
let _brace = braced!(value in input);
@@ -67,12 +72,12 @@ impl Prop {
Ok(Self {
label,
value: expr,
- is_forced_attribute,
+ directive,
})
}
/// Parse a prop of the form `label={value}`
- fn parse_prop_assignment(input: ParseStream, is_forced_attribute: bool) -> syn::Result {
+ fn parse_prop_assignment(input: ParseStream, directive: Option) -> syn::Result {
let label = input.parse::()?;
let equals = input.parse::().map_err(|_| {
syn::Error::new_spanned(
@@ -95,7 +100,7 @@ impl Prop {
Ok(Self {
label,
value,
- is_forced_attribute,
+ directive,
})
}
}
@@ -118,10 +123,10 @@ fn parse_prop_value(input: &ParseBuffer) -> syn::Result {
match &expr {
Expr::Lit(_) => Ok(expr),
- _ => Err(syn::Error::new_spanned(
+ ref exp => Err(syn::Error::new_spanned(
&expr,
- "the property value must be either a literal or enclosed in braces. Consider \
- adding braces around your expression.",
+ format!("the property value must be either a literal or enclosed in braces. Consider \
+ adding braces around your expression.: {:#?}", exp),
)),
}
}
diff --git a/packages/yew-macro/src/props/prop_macro.rs b/packages/yew-macro/src/props/prop_macro.rs
index 99e8948fb49..1ff4312276b 100644
--- a/packages/yew-macro/src/props/prop_macro.rs
+++ b/packages/yew-macro/src/props/prop_macro.rs
@@ -64,7 +64,7 @@ impl From for Prop {
Prop {
label,
value,
- is_forced_attribute: false,
+ directive: None,
}
}
}
diff --git a/packages/yew/src/dom_bundle/btag/attributes.rs b/packages/yew/src/dom_bundle/btag/attributes.rs
index d8c0fe86f5b..451f274a53b 100644
--- a/packages/yew/src/dom_bundle/btag/attributes.rs
+++ b/packages/yew/src/dom_bundle/btag/attributes.rs
@@ -145,8 +145,7 @@ impl Attributes {
Some(old) => old != new,
None => true,
} {
- // todo: set property
- el.set_attribute(k, new.0).unwrap();
+ Self::set(&el, k, new.0, new.1);
}
}
@@ -164,16 +163,9 @@ impl Attributes {
el.set_attribute(key, value).expect("invalid attribute key")
}
ApplyAttributeAs::Property => {
- match key {
- // need to be attributes because, otherwise query selectors fail
- "class" => el.set_attribute(key, value).expect("invalid attribute key"),
- _ => {
- let key = JsValue::from_str(key);
- let value = JsValue::from_str(value);
- js_sys::Reflect::set(el.as_ref(), &key, &value)
- .expect("could not set property");
- }
- }
+ let key = JsValue::from_str(key);
+ let value = JsValue::from_str(value);
+ js_sys::Reflect::set(el.as_ref(), &key, &value).expect("could not set property");
}
}
}
@@ -184,17 +176,9 @@ impl Attributes {
.remove_attribute(key)
.expect("could not remove attribute"),
ApplyAttributeAs::Property => {
- match key {
- // need to be attributes because, otherwise query selectors fail
- "class" => el
- .remove_attribute(key)
- .expect("could not remove attribute"),
- _ => {
- let key = JsValue::from_str(key);
- js_sys::Reflect::set(el.as_ref(), &key, &JsValue::UNDEFINED)
- .expect("could not remove property");
- }
- }
+ let key = JsValue::from_str(key);
+ js_sys::Reflect::set(el.as_ref(), &key, &JsValue::UNDEFINED)
+ .expect("could not remove property");
}
}
}
@@ -375,7 +359,7 @@ mod tests {
async fn macro_syntax_works() {
#[function_component]
fn Comp() -> Html {
- html! { }
+ html! { }
}
let output = gloo::utils::document().get_element_by_id("output").unwrap();
@@ -383,15 +367,15 @@ mod tests {
gloo::timers::future::sleep(Duration::from_secs(1)).await;
let element = output.query_selector("a").unwrap().unwrap();
- assert_eq!(element.get_attribute("alt").unwrap(), "abc");
+ assert_eq!(element.get_attribute("href").unwrap(), "https://example.com/");
assert_eq!(
- Reflect::get(element.as_ref(), &JsValue::from_str("href"))
- .expect("no href")
+ Reflect::get(element.as_ref(), &JsValue::from_str("alt"))
+ .expect("no alt")
.as_string()
.expect("not a string"),
- "https://example.com/",
- "property `href` not set properly"
+ "abc",
+ "property `alt` not set properly"
);
}
}
diff --git a/packages/yew/src/virtual_dom/mod.rs b/packages/yew/src/virtual_dom/mod.rs
index 37e2f9637e2..f24a2db6931 100644
--- a/packages/yew/src/virtual_dom/mod.rs
+++ b/packages/yew/src/virtual_dom/mod.rs
@@ -242,7 +242,7 @@ impl From> for Attributes {
fn from(map: IndexMap) -> Self {
let v = map
.into_iter()
- .map(|(k, v)| (k, (v, ApplyAttributeAs::Property)))
+ .map(|(k, v)| (k, (v, ApplyAttributeAs::Attribute)))
.collect();
Self::IndexMap(v)
}
@@ -252,7 +252,7 @@ impl From> for Attributes {
fn from(v: IndexMap<&'static str, AttrValue>) -> Self {
let v = v
.into_iter()
- .map(|(k, v)| (AttrValue::Static(k), (v, ApplyAttributeAs::Property)))
+ .map(|(k, v)| (AttrValue::Static(k), (v, ApplyAttributeAs::Attribute)))
.collect();
Self::IndexMap(v)
}
diff --git a/packages/yew/src/virtual_dom/vtag.rs b/packages/yew/src/virtual_dom/vtag.rs
index f9621fe59c5..a4e423a4f59 100644
--- a/packages/yew/src/virtual_dom/vtag.rs
+++ b/packages/yew/src/virtual_dom/vtag.rs
@@ -369,11 +369,10 @@ impl VTag {
);
}
- /// Adds a key-value pair to element's property
+ /// Set the given key as property on the element
///
- /// Not everything works when it set as an property. We use workarounds for:
- /// `class`, which is set as attribute.
- pub fn set_property(&mut self, key: &'static str, value: impl Into) {
+ /// [`js_sys::Reflect`] is used for setting properties.
+ pub fn add_property(&mut self, key: &'static str, value: impl Into) {
self.attributes.get_mut_index_map().insert(
AttrValue::Static(key),
(value.into(), ApplyAttributeAs::Property),