mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Add "struct update" syntax to pass props to component (..props instead of with props) (#2024)
* Reword to use double-dot syntax instead of "with" * Implement double-dot syntax for props in components * Update documentation with new syntax * Update forgotten doc * Add descriptive comments * Check props and base expression * Make compatible with 1.49.0 by removing then * Fix website tests * Update error output * Implicitly convert string literals to String if they are listed as props * Remove unused keyword * Rename function for checking if string literal * Fix weird formatting * Update code based on review * Update website/docs/concepts/html/components.md Co-authored-by: mc1098 <m.cripps1@uni.brighton.ac.uk> * Base expression span includes dot2 now * Improve specificity of error message * Chain together error messages * Add an example failure case to illustrate combined error message * Update based on review comments * Fix missing clones Co-authored-by: mc1098 <m.cripps1@uni.brighton.ac.uk>
This commit is contained in:
parent
58753d9607
commit
eeddcb95be
@ -68,16 +68,13 @@ impl Parse for HtmlComponent {
|
|||||||
input.parse::<HtmlComponentClose>()?;
|
input.parse::<HtmlComponentClose>()?;
|
||||||
|
|
||||||
if !children.is_empty() {
|
if !children.is_empty() {
|
||||||
// check if the `children` prop is given explicitly
|
if let Some(children_prop) = open.props.children() {
|
||||||
if let ComponentProps::List(props) = &open.props {
|
|
||||||
if let Some(children_prop) = props.get_by_label("children") {
|
|
||||||
return Err(syn::Error::new_spanned(
|
return Err(syn::Error::new_spanned(
|
||||||
&children_prop.label,
|
&children_prop.label,
|
||||||
"cannot specify the `children` prop when the component already has children",
|
"cannot specify the `children` prop when the component already has children",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(HtmlComponent {
|
Ok(HtmlComponent {
|
||||||
ty: open.ty,
|
ty: open.ty,
|
||||||
|
|||||||
@ -1,99 +1,52 @@
|
|||||||
use super::{Prop, Props, SpecialProps};
|
use super::{Prop, Props, SpecialProps, CHILDREN_LABEL};
|
||||||
use proc_macro2::{Ident, TokenStream, TokenTree};
|
use proc_macro2::{Ident, TokenStream};
|
||||||
use quote::{quote, quote_spanned, ToTokens};
|
use quote::{quote, quote_spanned, ToTokens};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use syn::{
|
use syn::{
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
spanned::Spanned,
|
spanned::Spanned,
|
||||||
Expr, Token,
|
token::Dot2,
|
||||||
|
Expr, ExprLit, Lit,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod kw {
|
struct BaseExpr {
|
||||||
syn::custom_keyword!(with);
|
pub dot2: Dot2,
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WithProps {
|
|
||||||
pub special: SpecialProps,
|
|
||||||
pub with: kw::with,
|
|
||||||
pub expr: Expr,
|
pub expr: Expr,
|
||||||
}
|
}
|
||||||
impl WithProps {
|
|
||||||
/// Check if the `ParseStream` contains a `with expr` expression.
|
|
||||||
/// This function advances the given `ParseStream`!
|
|
||||||
fn contains_with_expr(input: ParseStream) -> bool {
|
|
||||||
while !input.is_empty() {
|
|
||||||
if input.peek(kw::with) && !input.peek2(Token![=]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
input.parse::<TokenTree>().ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
impl Parse for BaseExpr {
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Parse for WithProps {
|
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
let mut special = SpecialProps::default();
|
let dot2 = input.parse()?;
|
||||||
let mut with_expr: Option<(kw::with, Expr)> = None;
|
let expr = input.parse().map_err(|expr_error| {
|
||||||
while !input.is_empty() {
|
let mut error =
|
||||||
// no need to check if it's followed by `=` because `with` isn't a special prop
|
syn::Error::new_spanned(dot2, "expected base props expression after `..`");
|
||||||
if input.peek(kw::with) {
|
error.combine(expr_error);
|
||||||
if let Some((with, expr)) = with_expr {
|
error
|
||||||
return Err(syn::Error::new_spanned(
|
})?;
|
||||||
quote! { #with#expr },
|
Ok(Self { dot2, expr })
|
||||||
"there are two `with <props>` definitions for this component (note: you can only define `with <props>` once)"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let with = input.parse::<kw::with>()?;
|
|
||||||
if input.is_empty() {
|
|
||||||
return Err(syn::Error::new_spanned(
|
|
||||||
with,
|
|
||||||
"expected expression following this `with`",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
with_expr = Some((with, input.parse()?));
|
|
||||||
} else {
|
|
||||||
let prop = input.parse::<Prop>()?;
|
|
||||||
|
|
||||||
if let Some(slot) = special.get_slot_mut(&prop.label.to_string()) {
|
|
||||||
if slot.is_some() {
|
|
||||||
return Err(syn::Error::new_spanned(
|
|
||||||
&prop.label,
|
|
||||||
&format!("`{}` can only be set once", prop.label),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
slot.replace(prop);
|
|
||||||
} else {
|
|
||||||
return Err(syn::Error::new_spanned(
|
|
||||||
prop.label,
|
|
||||||
"Using the `with props` syntax in combination with named props is not allowed (note: this does not apply to special props like `ref` and `key`)",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (with, expr) =
|
|
||||||
with_expr.ok_or_else(|| input.error("missing `with props` expression"))?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
special,
|
|
||||||
with,
|
|
||||||
expr,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ComponentProps {
|
impl ToTokens for BaseExpr {
|
||||||
List(Props),
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
With(Box<WithProps>),
|
let BaseExpr { dot2, expr } = self;
|
||||||
|
tokens.extend(quote! { #dot2#expr });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ComponentProps {
|
||||||
|
props: Props,
|
||||||
|
base_expr: Option<Expr>,
|
||||||
}
|
}
|
||||||
impl ComponentProps {
|
impl ComponentProps {
|
||||||
/// Get the special props supported by both variants
|
/// Get the special props supported by both variants
|
||||||
pub fn special(&self) -> &SpecialProps {
|
pub fn special(&self) -> &SpecialProps {
|
||||||
match self {
|
&self.props.special
|
||||||
Self::List(props) => &props.special,
|
|
||||||
Self::With(props) => &props.special,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if the `children` prop is given explicitly
|
||||||
|
pub fn children(&self) -> Option<&Prop> {
|
||||||
|
self.props.get_by_label(CHILDREN_LABEL)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prop_validation_tokens(&self, props_ty: impl ToTokens, has_children: bool) -> TokenStream {
|
fn prop_validation_tokens(&self, props_ty: impl ToTokens, has_children: bool) -> TokenStream {
|
||||||
@ -103,20 +56,16 @@ impl ComponentProps {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let check_props = match self {
|
let check_props: TokenStream = self
|
||||||
Self::List(props) => props
|
.props
|
||||||
.iter()
|
.iter()
|
||||||
.map(|Prop { label, .. }| {
|
.map(|Prop { label, .. }| quote_spanned! ( label.span()=> __yew_props.#label; ))
|
||||||
quote_spanned! {label.span()=> __yew_props.#label; }
|
.chain(self.base_expr.iter().map(|expr| {
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
Self::With(with_props) => {
|
|
||||||
let expr = &with_props.expr;
|
|
||||||
quote_spanned! {props_ty.span()=>
|
quote_spanned! {props_ty.span()=>
|
||||||
let _: #props_ty = #expr;
|
let _: #props_ty = #expr;
|
||||||
}
|
}
|
||||||
}
|
}))
|
||||||
};
|
.collect();
|
||||||
|
|
||||||
quote_spanned! {props_ty.span()=>
|
quote_spanned! {props_ty.span()=>
|
||||||
#[allow(clippy::no_effect)]
|
#[allow(clippy::no_effect)]
|
||||||
@ -135,9 +84,9 @@ impl ComponentProps {
|
|||||||
children_renderer: Option<CR>,
|
children_renderer: Option<CR>,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let validate_props = self.prop_validation_tokens(&props_ty, children_renderer.is_some());
|
let validate_props = self.prop_validation_tokens(&props_ty, children_renderer.is_some());
|
||||||
let build_props = match self {
|
let build_props = match &self.base_expr {
|
||||||
Self::List(props) => {
|
None => {
|
||||||
let set_props = props.iter().map(|Prop { label, value, .. }| {
|
let set_props = self.props.iter().map(|Prop { label, value, .. }| {
|
||||||
quote_spanned! {value.span()=>
|
quote_spanned! {value.span()=>
|
||||||
.#label(#value)
|
.#label(#value)
|
||||||
}
|
}
|
||||||
@ -156,17 +105,31 @@ impl ComponentProps {
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::With(with_props) => {
|
// Builder pattern is unnecessary in this case, since the base expression guarantees
|
||||||
|
// all values are initialized
|
||||||
|
Some(expr) => {
|
||||||
let ident = Ident::new("__yew_props", props_ty.span());
|
let ident = Ident::new("__yew_props", props_ty.span());
|
||||||
|
let set_props = self.props.iter().map(|Prop { label, value, .. }| {
|
||||||
|
if is_string_literal(value) {
|
||||||
|
// String literals should be implicitly converted into `String`
|
||||||
|
quote_spanned! {value.span()=>
|
||||||
|
#ident.#label = ::std::convert::Into::into(#value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote_spanned! {value.span()=>
|
||||||
|
#ident.#label = #value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
let set_children = children_renderer.map(|children| {
|
let set_children = children_renderer.map(|children| {
|
||||||
quote_spanned! {props_ty.span()=>
|
quote_spanned! {props_ty.span()=>
|
||||||
#ident.children = #children;
|
#ident.children = #children;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let expr = &with_props.expr;
|
|
||||||
quote! {
|
quote! {
|
||||||
let mut #ident = #expr;
|
let mut #ident = #expr;
|
||||||
|
#(#set_props)*
|
||||||
#set_children
|
#set_children
|
||||||
#ident
|
#ident
|
||||||
}
|
}
|
||||||
@ -181,12 +144,34 @@ impl ComponentProps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_string_literal(expr: &Expr) -> bool {
|
||||||
|
matches!(
|
||||||
|
expr,
|
||||||
|
Expr::Lit(ExprLit {
|
||||||
|
lit: Lit::Str(_),
|
||||||
|
..
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
impl Parse for ComponentProps {
|
impl Parse for ComponentProps {
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
if WithProps::contains_with_expr(&input.fork()) {
|
let props = validate(input.parse()?)?;
|
||||||
input.parse().map(Self::With)
|
let base_expr = if input.is_empty() {
|
||||||
|
None
|
||||||
} else {
|
} else {
|
||||||
input.parse::<Props>().and_then(Self::try_from)
|
Some(input.parse::<BaseExpr>()?)
|
||||||
|
};
|
||||||
|
|
||||||
|
if input.is_empty() {
|
||||||
|
let base_expr = base_expr.map(|base| base.expr);
|
||||||
|
Ok(Self { props, base_expr })
|
||||||
|
} else {
|
||||||
|
Err(syn::Error::new_spanned(
|
||||||
|
base_expr,
|
||||||
|
"base props expression must appear last in list of props",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,6 +180,14 @@ impl TryFrom<Props> for ComponentProps {
|
|||||||
type Error = syn::Error;
|
type Error = syn::Error;
|
||||||
|
|
||||||
fn try_from(props: Props) -> Result<Self, Self::Error> {
|
fn try_from(props: Props) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
props: validate(props)?,
|
||||||
|
base_expr: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate(props: Props) -> Result<Props, syn::Error> {
|
||||||
props.check_no_duplicates()?;
|
props.check_no_duplicates()?;
|
||||||
props.check_all(|prop| {
|
props.check_all(|prop| {
|
||||||
if !prop.label.extended.is_empty() {
|
if !prop.label.extended.is_empty() {
|
||||||
@ -207,6 +200,5 @@ impl TryFrom<Props> for ComponentProps {
|
|||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Self::List(props))
|
Ok(props)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,3 +7,5 @@ pub use component::*;
|
|||||||
pub use element::*;
|
pub use element::*;
|
||||||
pub use prop::*;
|
pub use prop::*;
|
||||||
pub use prop_macro::PropsMacroInput;
|
pub use prop_macro::PropsMacroInput;
|
||||||
|
|
||||||
|
const CHILDREN_LABEL: &str = "children";
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
|
use super::CHILDREN_LABEL;
|
||||||
use crate::html_tree::HtmlDashedName;
|
use crate::html_tree::HtmlDashedName;
|
||||||
|
use proc_macro2::{Spacing, TokenTree};
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
@ -6,9 +8,9 @@ use std::{
|
|||||||
};
|
};
|
||||||
use syn::{
|
use syn::{
|
||||||
braced,
|
braced,
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseBuffer, ParseStream},
|
||||||
token::Brace,
|
token::Brace,
|
||||||
Block, Expr, ExprBlock, ExprPath, Stmt, Token,
|
Block, Expr, ExprBlock, ExprPath, ExprRange, Stmt, Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Prop {
|
pub struct Prop {
|
||||||
@ -74,14 +76,46 @@ impl Prop {
|
|||||||
"expected an expression following this equals sign",
|
"expected an expression following this equals sign",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let value = strip_braces(input.parse::<Expr>()?)?;
|
|
||||||
|
let value = parse_prop_value(input)?;
|
||||||
Ok(Self { label, value })
|
Ok(Self { label, value })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strip_braces(expr: Expr) -> syn::Result<Expr> {
|
fn parse_prop_value(input: &ParseBuffer) -> syn::Result<Expr> {
|
||||||
match expr {
|
if input.peek(Brace) {
|
||||||
Expr::Block(ExprBlock { block: Block { mut stmts, .. }, .. }) if stmts.len() == 1 => {
|
strip_braces(input.parse()?)
|
||||||
|
} else {
|
||||||
|
let expr = if let Some(ExprRange {
|
||||||
|
from: Some(from), ..
|
||||||
|
}) = range_expression_peek(input)
|
||||||
|
{
|
||||||
|
// If a range expression is seen, treat the left-side expression as the value
|
||||||
|
// and leave the right-side expression to be parsed as a base expression
|
||||||
|
advance_until_next_dot2(input)?;
|
||||||
|
*from
|
||||||
|
} else {
|
||||||
|
input.parse()?
|
||||||
|
};
|
||||||
|
|
||||||
|
match &expr {
|
||||||
|
Expr::Lit(_) => Ok(expr),
|
||||||
|
_ => {
|
||||||
|
Err(syn::Error::new_spanned(
|
||||||
|
&expr,
|
||||||
|
"the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn strip_braces(block: ExprBlock) -> syn::Result<Expr> {
|
||||||
|
match block {
|
||||||
|
ExprBlock {
|
||||||
|
block: Block { mut stmts, .. },
|
||||||
|
..
|
||||||
|
} if stmts.len() == 1 => {
|
||||||
let stmt = stmts.remove(0);
|
let stmt = stmts.remove(0);
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Expr(expr) => Ok(expr),
|
Stmt::Expr(expr) => Ok(expr),
|
||||||
@ -95,14 +129,46 @@ fn strip_braces(expr: Expr) -> syn::Result<Expr> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Lit(_) | Expr::Block(_) => Ok(expr),
|
block => Ok(Expr::Block(block)),
|
||||||
_ => Err(syn::Error::new_spanned(
|
|
||||||
&expr,
|
|
||||||
"the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.".to_string(),
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Without advancing cursor, returns the range expression at the current cursor position if any
|
||||||
|
fn range_expression_peek(input: &ParseBuffer) -> Option<ExprRange> {
|
||||||
|
match input.fork().parse::<Expr>().ok()? {
|
||||||
|
Expr::Range(range) => Some(range),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn advance_until_next_dot2(input: &ParseBuffer) -> syn::Result<()> {
|
||||||
|
input.step(|cursor| {
|
||||||
|
let mut rest = *cursor;
|
||||||
|
let mut first_dot = None;
|
||||||
|
while let Some((tt, next)) = rest.token_tree() {
|
||||||
|
match &tt {
|
||||||
|
TokenTree::Punct(punct) if punct.as_char() == '.' => {
|
||||||
|
if let Some(first_dot) = first_dot {
|
||||||
|
return Ok(((), first_dot));
|
||||||
|
} else {
|
||||||
|
// Only consider dot as potential first if there is no spacing after it
|
||||||
|
first_dot = if punct.spacing() == Spacing::Joint {
|
||||||
|
Some(rest)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
first_dot = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rest = next;
|
||||||
|
}
|
||||||
|
Err(cursor.error("no `..` found in expression"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// List of props sorted in alphabetical order*.
|
/// List of props sorted in alphabetical order*.
|
||||||
///
|
///
|
||||||
/// \*The "children" prop always comes last to match the behaviour of the `Properties` derive macro.
|
/// \*The "children" prop always comes last to match the behaviour of the `Properties` derive macro.
|
||||||
@ -111,8 +177,6 @@ fn strip_braces(expr: Expr) -> syn::Result<Expr> {
|
|||||||
/// Use `check_no_duplicates` to ensure that there are no duplicates.
|
/// Use `check_no_duplicates` to ensure that there are no duplicates.
|
||||||
pub struct SortedPropList(Vec<Prop>);
|
pub struct SortedPropList(Vec<Prop>);
|
||||||
impl SortedPropList {
|
impl SortedPropList {
|
||||||
const CHILDREN_LABEL: &'static str = "children";
|
|
||||||
|
|
||||||
/// Create a new `SortedPropList` from a vector of props.
|
/// Create a new `SortedPropList` from a vector of props.
|
||||||
/// The given `props` doesn't need to be sorted.
|
/// The given `props` doesn't need to be sorted.
|
||||||
pub fn new(mut props: Vec<Prop>) -> Self {
|
pub fn new(mut props: Vec<Prop>) -> Self {
|
||||||
@ -123,9 +187,9 @@ impl SortedPropList {
|
|||||||
fn cmp_label(a: &str, b: &str) -> Ordering {
|
fn cmp_label(a: &str, b: &str) -> Ordering {
|
||||||
if a == b {
|
if a == b {
|
||||||
Ordering::Equal
|
Ordering::Equal
|
||||||
} else if a == Self::CHILDREN_LABEL {
|
} else if a == CHILDREN_LABEL {
|
||||||
Ordering::Greater
|
Ordering::Greater
|
||||||
} else if b == Self::CHILDREN_LABEL {
|
} else if b == CHILDREN_LABEL {
|
||||||
Ordering::Less
|
Ordering::Less
|
||||||
} else {
|
} else {
|
||||||
a.cmp(b)
|
a.cmp(b)
|
||||||
@ -210,7 +274,8 @@ impl SortedPropList {
|
|||||||
impl Parse for SortedPropList {
|
impl Parse for SortedPropList {
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
let mut props: Vec<Prop> = Vec::new();
|
let mut props: Vec<Prop> = Vec::new();
|
||||||
while !input.is_empty() {
|
// Stop parsing props if a base expression preceded by `..` is reached
|
||||||
|
while !input.is_empty() && !input.peek(Token![..]) {
|
||||||
props.push(input.parse()?);
|
props.push(input.parse()?);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,14 +305,6 @@ impl SpecialProps {
|
|||||||
Ok(Self { node_ref, key })
|
Ok(Self { node_ref, key })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_slot_mut(&mut self, key: &str) -> Option<&mut Option<Prop>> {
|
|
||||||
match key {
|
|
||||||
Self::REF_LABEL => Some(&mut self.node_ref),
|
|
||||||
Self::KEY_LABEL => Some(&mut self.key),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn iter(&self) -> impl Iterator<Item = &Prop> {
|
fn iter(&self) -> impl Iterator<Item = &Prop> {
|
||||||
self.node_ref.as_ref().into_iter().chain(self.key.as_ref())
|
self.node_ref.as_ref().into_iter().chain(self.key.as_ref())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,20 +43,31 @@ fn compile_fail() {
|
|||||||
html! { <Child> };
|
html! { <Child> };
|
||||||
html! { <Child:: /> };
|
html! { <Child:: /> };
|
||||||
html! { <Child with /> };
|
html! { <Child with /> };
|
||||||
|
html! { <Child .. /> };
|
||||||
|
html! { <Child ..{ 5 + } /> };
|
||||||
html! { <Child props /> };
|
html! { <Child props /> };
|
||||||
html! { <Child with props > };
|
html! { <Child with props > };
|
||||||
|
html! { <Child ..props > };
|
||||||
let (p1, p2);
|
let (p1, p2);
|
||||||
html! { <Child with p1 with p2 /> };
|
html! { <Child with p1 with p2 /> };
|
||||||
|
html! { <Child ..p1 ..p2 /> };
|
||||||
html! { <Child with props ref={()} ref={()} /> };
|
html! { <Child with props ref={()} ref={()} /> };
|
||||||
|
html! { <Child ..props ref={()} ref={()} /> };
|
||||||
html! { <Child with props ref={()} ref={()} value=1 /> };
|
html! { <Child with props ref={()} ref={()} value=1 /> };
|
||||||
|
html! { <Child ..props ref={()} ref={()} value=1 /> };
|
||||||
html! { <Child with props ref={()} value=1 ref={()} /> };
|
html! { <Child with props ref={()} value=1 ref={()} /> };
|
||||||
|
html! { <Child ..props ref={()} value=1 ref={()} /> };
|
||||||
html! { <Child with props value=1 ref={()} ref={()} /> };
|
html! { <Child with props value=1 ref={()} ref={()} /> };
|
||||||
|
html! { <Child ..props value=1 ref={()} ref={()} /> };
|
||||||
html! { <Child value=1 with props ref={()} ref={()} /> };
|
html! { <Child value=1 with props ref={()} ref={()} /> };
|
||||||
|
html! { <Child value=1 ..props ref={()} ref={()} /> };
|
||||||
html! { <Child value=1 ref={()} with props ref={()} /> };
|
html! { <Child value=1 ref={()} with props ref={()} /> };
|
||||||
|
html! { <Child value=1 ref={()} ..props ref={()} /> };
|
||||||
html! { <Child ref={()} ref={()} value=1 with props /> };
|
html! { <Child ref={()} ref={()} value=1 with props /> };
|
||||||
html! { <Child with blah /> };
|
html! { <Child ref={()} ref={()} value=1 ..props /> };
|
||||||
html! { <Child value=1 with props /> };
|
html! { <Child ..blah /> };
|
||||||
html! { <Child with props value=1 /> };
|
html! { <Child value=1 ..props /> };
|
||||||
|
html! { <Child .. props value=1 /> };
|
||||||
html! { <Child type=0 /> };
|
html! { <Child type=0 /> };
|
||||||
html! { <Child ref=() /> };
|
html! { <Child ref=() /> };
|
||||||
html! { <Child invalid-prop-name=0 /> };
|
html! { <Child invalid-prop-name=0 /> };
|
||||||
@ -75,9 +86,12 @@ fn compile_fail() {
|
|||||||
html! { <Child></Child><Child></Child> };
|
html! { <Child></Child><Child></Child> };
|
||||||
html! { <Child>{ "Not allowed" }</Child> };
|
html! { <Child>{ "Not allowed" }</Child> };
|
||||||
|
|
||||||
|
let num = 1;
|
||||||
|
html! { <Child int=num ..props /> };
|
||||||
|
|
||||||
// trying to overwrite `children` on props which don't take any.
|
// trying to overwrite `children` on props which don't take any.
|
||||||
html! {
|
html! {
|
||||||
<Child with ChildProperties { string: "hello".to_owned(), int: 5 }>
|
<Child ..ChildProperties { string: "hello".to_owned(), int: 5 }>
|
||||||
{ "please error" }
|
{ "please error" }
|
||||||
</Child>
|
</Child>
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,229 +10,335 @@ error: unexpected end of input, expected identifier
|
|||||||
44 | html! { <Child:: /> };
|
44 | html! { <Child:: /> };
|
||||||
| ^^^^^^^^^^^
|
| ^^^^^^^^^^^
|
||||||
|
|
||||||
error: expected expression following this `with`
|
error: `with` doesn't have a value. (hint: set the value to `true` or `false` for boolean attributes)
|
||||||
--> $DIR/component-fail.rs:45:20
|
--> $DIR/component-fail.rs:45:20
|
||||||
|
|
|
|
||||||
45 | html! { <Child with /> };
|
45 | html! { <Child with /> };
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
||||||
error: `props` doesn't have a value. (hint: set the value to `true` or `false` for boolean attributes)
|
error: expected base props expression after `..`
|
||||||
--> $DIR/component-fail.rs:46:20
|
--> $DIR/component-fail.rs:46:20
|
||||||
|
|
|
|
||||||
46 | html! { <Child props /> };
|
46 | html! { <Child .. /> };
|
||||||
| ^^^^^
|
| ^^
|
||||||
|
|
||||||
error: this opening tag has no corresponding closing tag
|
error: unexpected end of input, expected expression
|
||||||
--> $DIR/component-fail.rs:47:13
|
--> $DIR/component-fail.rs:46:13
|
||||||
|
|
|
|
||||||
47 | html! { <Child with props > };
|
46 | html! { <Child .. /> };
|
||||||
| ^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
error: there are two `with <props>` definitions for this component (note: you can only define `with <props>` once)
|
error: expected base props expression after `..`
|
||||||
--> $DIR/component-fail.rs:49:20
|
--> $DIR/component-fail.rs:47:20
|
||||||
|
|
|
|
||||||
49 | html! { <Child with p1 with p2 /> };
|
47 | html! { <Child ..{ 5 + } /> };
|
||||||
|
| ^^
|
||||||
|
|
||||||
|
error: unexpected end of input, expected expression
|
||||||
|
--> $DIR/component-fail.rs:47:22
|
||||||
|
|
|
||||||
|
47 | html! { <Child ..{ 5 + } /> };
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
|
|
||||||
error: `ref` can only be set once
|
error: `props` doesn't have a value. (hint: set the value to `true` or `false` for boolean attributes)
|
||||||
--> $DIR/component-fail.rs:50:40
|
--> $DIR/component-fail.rs:48:20
|
||||||
|
|
|
|
||||||
50 | html! { <Child with props ref={()} ref={()} /> };
|
48 | html! { <Child props /> };
|
||||||
| ^^^
|
|
||||||
|
|
||||||
error: `ref` can only be set once
|
|
||||||
--> $DIR/component-fail.rs:51:40
|
|
||||||
|
|
|
||||||
51 | html! { <Child with props ref={()} ref={()} value=1 /> };
|
|
||||||
| ^^^
|
|
||||||
|
|
||||||
error: Using the `with props` syntax in combination with named props is not allowed (note: this does not apply to special props like `ref` and `key`)
|
|
||||||
--> $DIR/component-fail.rs:52:40
|
|
||||||
|
|
|
||||||
52 | html! { <Child with props ref={()} value=1 ref={()} /> };
|
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
||||||
error: Using the `with props` syntax in combination with named props is not allowed (note: this does not apply to special props like `ref` and `key`)
|
error: `with` doesn't have a value. (hint: set the value to `true` or `false` for boolean attributes)
|
||||||
--> $DIR/component-fail.rs:53:31
|
--> $DIR/component-fail.rs:49:20
|
||||||
|
|
|
|
||||||
53 | html! { <Child with props value=1 ref={()} ref={()} /> };
|
49 | html! { <Child with props > };
|
||||||
| ^^^^^
|
| ^^^^
|
||||||
|
|
||||||
error: Using the `with props` syntax in combination with named props is not allowed (note: this does not apply to special props like `ref` and `key`)
|
error: this opening tag has no corresponding closing tag
|
||||||
|
--> $DIR/component-fail.rs:50:13
|
||||||
|
|
|
||||||
|
50 | html! { <Child ..props > };
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: `with` doesn't have a value. (hint: set the value to `true` or `false` for boolean attributes)
|
||||||
|
--> $DIR/component-fail.rs:52:20
|
||||||
|
|
|
||||||
|
52 | html! { <Child with p1 with p2 /> };
|
||||||
|
| ^^^^
|
||||||
|
|
||||||
|
error: `with` doesn't have a value. (hint: set the value to `true` or `false` for boolean attributes)
|
||||||
--> $DIR/component-fail.rs:54:20
|
--> $DIR/component-fail.rs:54:20
|
||||||
|
|
|
|
||||||
54 | html! { <Child value=1 with props ref={()} ref={()} /> };
|
54 | html! { <Child with props ref={()} ref={()} /> };
|
||||||
| ^^^^^
|
| ^^^^
|
||||||
|
|
||||||
error: Using the `with props` syntax in combination with named props is not allowed (note: this does not apply to special props like `ref` and `key`)
|
error: base props expression must appear last in list of props
|
||||||
--> $DIR/component-fail.rs:55:20
|
--> $DIR/component-fail.rs:55:20
|
||||||
|
|
|
|
||||||
55 | html! { <Child value=1 ref={()} with props ref={()} /> };
|
55 | html! { <Child ..props ref={()} ref={()} /> };
|
||||||
| ^^^^^
|
| ^^^^^^^
|
||||||
|
|
||||||
error: `ref` can only be set once
|
error: `with` doesn't have a value. (hint: set the value to `true` or `false` for boolean attributes)
|
||||||
--> $DIR/component-fail.rs:56:29
|
--> $DIR/component-fail.rs:56:20
|
||||||
|
|
|
|
||||||
56 | html! { <Child ref={()} ref={()} value=1 with props /> };
|
56 | html! { <Child with props ref={()} ref={()} value=1 /> };
|
||||||
| ^^^
|
| ^^^^
|
||||||
|
|
||||||
error: Using the `with props` syntax in combination with named props is not allowed (note: this does not apply to special props like `ref` and `key`)
|
error: base props expression must appear last in list of props
|
||||||
|
--> $DIR/component-fail.rs:57:20
|
||||||
|
|
|
||||||
|
57 | html! { <Child ..props ref={()} ref={()} value=1 /> };
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: `with` doesn't have a value. (hint: set the value to `true` or `false` for boolean attributes)
|
||||||
--> $DIR/component-fail.rs:58:20
|
--> $DIR/component-fail.rs:58:20
|
||||||
|
|
|
|
||||||
58 | html! { <Child value=1 with props /> };
|
58 | html! { <Child with props ref={()} value=1 ref={()} /> };
|
||||||
| ^^^^^
|
| ^^^^
|
||||||
|
|
||||||
error: Using the `with props` syntax in combination with named props is not allowed (note: this does not apply to special props like `ref` and `key`)
|
error: base props expression must appear last in list of props
|
||||||
--> $DIR/component-fail.rs:59:31
|
--> $DIR/component-fail.rs:59:20
|
||||||
|
|
|
|
||||||
59 | html! { <Child with props value=1 /> };
|
59 | html! { <Child ..props ref={()} value=1 ref={()} /> };
|
||||||
| ^^^^^
|
| ^^^^^^^
|
||||||
|
|
||||||
error: expected identifier, found keyword `type`
|
error: `with` doesn't have a value. (hint: set the value to `true` or `false` for boolean attributes)
|
||||||
--> $DIR/component-fail.rs:60:20
|
--> $DIR/component-fail.rs:60:20
|
||||||
|
|
|
|
||||||
60 | html! { <Child type=0 /> };
|
60 | html! { <Child with props value=1 ref={()} ref={()} /> };
|
||||||
|
| ^^^^
|
||||||
|
|
||||||
|
error: base props expression must appear last in list of props
|
||||||
|
--> $DIR/component-fail.rs:61:20
|
||||||
|
|
|
||||||
|
61 | html! { <Child ..props value=1 ref={()} ref={()} /> };
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: `with` doesn't have a value. (hint: set the value to `true` or `false` for boolean attributes)
|
||||||
|
--> $DIR/component-fail.rs:62:28
|
||||||
|
|
|
||||||
|
62 | html! { <Child value=1 with props ref={()} ref={()} /> };
|
||||||
|
| ^^^^
|
||||||
|
|
||||||
|
error: base props expression must appear last in list of props
|
||||||
|
--> $DIR/component-fail.rs:63:28
|
||||||
|
|
|
||||||
|
63 | html! { <Child value=1 ..props ref={()} ref={()} /> };
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: `with` doesn't have a value. (hint: set the value to `true` or `false` for boolean attributes)
|
||||||
|
--> $DIR/component-fail.rs:64:37
|
||||||
|
|
|
||||||
|
64 | html! { <Child value=1 ref={()} with props ref={()} /> };
|
||||||
|
| ^^^^
|
||||||
|
|
||||||
|
error: base props expression must appear last in list of props
|
||||||
|
--> $DIR/component-fail.rs:65:37
|
||||||
|
|
|
||||||
|
65 | html! { <Child value=1 ref={()} ..props ref={()} /> };
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: `with` doesn't have a value. (hint: set the value to `true` or `false` for boolean attributes)
|
||||||
|
--> $DIR/component-fail.rs:66:47
|
||||||
|
|
|
||||||
|
66 | html! { <Child ref={()} ref={()} value=1 with props /> };
|
||||||
|
| ^^^^
|
||||||
|
|
||||||
|
error: `ref` can only be specified once
|
||||||
|
--> $DIR/component-fail.rs:67:20
|
||||||
|
|
|
||||||
|
67 | html! { <Child ref={()} ref={()} value=1 ..props /> };
|
||||||
|
| ^^^
|
||||||
|
|
||||||
|
error: base props expression must appear last in list of props
|
||||||
|
--> $DIR/component-fail.rs:70:20
|
||||||
|
|
|
||||||
|
70 | html! { <Child .. props value=1 /> };
|
||||||
|
| ^^^^^^^^
|
||||||
|
|
||||||
|
error: expected identifier, found keyword `type`
|
||||||
|
--> $DIR/component-fail.rs:71:20
|
||||||
|
|
|
||||||
|
71 | html! { <Child type=0 /> };
|
||||||
| ^^^^ expected identifier, found keyword
|
| ^^^^ expected identifier, found keyword
|
||||||
|
|
|
|
||||||
help: you can escape reserved keywords to use them as identifiers
|
help: you can escape reserved keywords to use them as identifiers
|
||||||
|
|
|
|
||||||
60 | html! { <Child r#type=0 /> };
|
71 | html! { <Child r#type=0 /> };
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|
||||||
error: the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.
|
error: the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.
|
||||||
--> $DIR/component-fail.rs:61:24
|
--> $DIR/component-fail.rs:72:24
|
||||||
|
|
|
|
||||||
61 | html! { <Child ref=() /> };
|
72 | html! { <Child ref=() /> };
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: expected a valid Rust identifier
|
error: expected a valid Rust identifier
|
||||||
--> $DIR/component-fail.rs:62:20
|
--> $DIR/component-fail.rs:73:20
|
||||||
|
|
|
|
||||||
62 | html! { <Child invalid-prop-name=0 /> };
|
73 | html! { <Child invalid-prop-name=0 /> };
|
||||||
| ^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: expected an expression following this equals sign
|
error: expected an expression following this equals sign
|
||||||
--> $DIR/component-fail.rs:64:26
|
--> $DIR/component-fail.rs:75:26
|
||||||
|
|
|
|
||||||
64 | html! { <Child string= /> };
|
75 | html! { <Child string= /> };
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: `int` can only be specified once but is given here again
|
error: `int` can only be specified once but is given here again
|
||||||
--> $DIR/component-fail.rs:65:26
|
--> $DIR/component-fail.rs:76:26
|
||||||
|
|
|
|
||||||
65 | html! { <Child int=1 int=2 int=3 /> };
|
76 | html! { <Child int=1 int=2 int=3 /> };
|
||||||
| ^^^
|
| ^^^
|
||||||
|
|
||||||
error: `int` can only be specified once but is given here again
|
error: `int` can only be specified once but is given here again
|
||||||
--> $DIR/component-fail.rs:65:32
|
--> $DIR/component-fail.rs:76:32
|
||||||
|
|
|
|
||||||
65 | html! { <Child int=1 int=2 int=3 /> };
|
76 | html! { <Child int=1 int=2 int=3 /> };
|
||||||
| ^^^
|
| ^^^
|
||||||
|
|
||||||
error: `ref` can only be specified once
|
error: `ref` can only be specified once
|
||||||
--> $DIR/component-fail.rs:70:26
|
--> $DIR/component-fail.rs:81:26
|
||||||
|
|
|
|
||||||
70 | html! { <Child int=1 ref={()} ref={()} /> };
|
81 | html! { <Child int=1 ref={()} ref={()} /> };
|
||||||
| ^^^
|
| ^^^
|
||||||
|
|
||||||
error: this closing tag has no corresponding opening tag
|
error: this closing tag has no corresponding opening tag
|
||||||
--> $DIR/component-fail.rs:73:13
|
--> $DIR/component-fail.rs:84:13
|
||||||
|
|
|
|
||||||
73 | html! { </Child> };
|
84 | html! { </Child> };
|
||||||
| ^^^^^^^^
|
| ^^^^^^^^
|
||||||
|
|
||||||
error: this opening tag has no corresponding closing tag
|
error: this opening tag has no corresponding closing tag
|
||||||
--> $DIR/component-fail.rs:74:13
|
--> $DIR/component-fail.rs:85:13
|
||||||
|
|
|
|
||||||
74 | html! { <Child><Child></Child> };
|
85 | html! { <Child><Child></Child> };
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
|
|
||||||
error: only one root html element is allowed (hint: you can wrap multiple html elements in a fragment `<></>`)
|
error: only one root html element is allowed (hint: you can wrap multiple html elements in a fragment `<></>`)
|
||||||
--> $DIR/component-fail.rs:75:28
|
--> $DIR/component-fail.rs:86:28
|
||||||
|
|
|
|
||||||
75 | html! { <Child></Child><Child></Child> };
|
86 | html! { <Child></Child><Child></Child> };
|
||||||
| ^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: cannot specify the `children` prop when the component already has children
|
error: the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.
|
||||||
--> $DIR/component-fail.rs:94:26
|
--> $DIR/component-fail.rs:90:24
|
||||||
|
|
|
|
||||||
94 | <ChildContainer {children}>
|
90 | html! { <Child int=num ..props /> };
|
||||||
|
| ^^^
|
||||||
|
|
||||||
|
error: cannot specify the `children` prop when the component already has children
|
||||||
|
--> $DIR/component-fail.rs:108:26
|
||||||
|
|
|
||||||
|
108 | <ChildContainer {children}>
|
||||||
| ^^^^^^^^
|
| ^^^^^^^^
|
||||||
|
|
||||||
error: only one root html element is allowed (hint: you can wrap multiple html elements in a fragment `<></>`)
|
error: only one root html element is allowed (hint: you can wrap multiple html elements in a fragment `<></>`)
|
||||||
--> $DIR/component-fail.rs:101:9
|
--> $DIR/component-fail.rs:115:9
|
||||||
|
|
|
|
||||||
101 | <span>{ 2 }</span>
|
115 | <span>{ 2 }</span>
|
||||||
| ^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: only simple identifiers are allowed in the shorthand property syntax
|
error: only simple identifiers are allowed in the shorthand property syntax
|
||||||
--> $DIR/component-fail.rs:104:21
|
--> $DIR/component-fail.rs:118:21
|
||||||
|
|
|
|
||||||
104 | html! { <Child {std::f64::consts::PI} /> };
|
118 | html! { <Child {std::f64::consts::PI} /> };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: missing label for property value. If trying to use the shorthand property syntax, only identifiers may be used
|
error: missing label for property value. If trying to use the shorthand property syntax, only identifiers may be used
|
||||||
--> $DIR/component-fail.rs:105:21
|
--> $DIR/component-fail.rs:119:21
|
||||||
|
|
|
|
||||||
105 | html! { <Child {7 + 6} /> };
|
119 | html! { <Child {7 + 6} /> };
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
||||||
error: missing label for property value. If trying to use the shorthand property syntax, only identifiers may be used
|
error: missing label for property value. If trying to use the shorthand property syntax, only identifiers may be used
|
||||||
--> $DIR/component-fail.rs:106:21
|
--> $DIR/component-fail.rs:120:21
|
||||||
|
|
|
|
||||||
106 | html! { <Child {children.len()} /> };
|
120 | html! { <Child {children.len()} /> };
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error[E0425]: cannot find value `blah` in this scope
|
error[E0425]: cannot find value `blah` in this scope
|
||||||
--> $DIR/component-fail.rs:57:25
|
--> $DIR/component-fail.rs:68:22
|
||||||
|
|
|
|
||||||
57 | html! { <Child with blah /> };
|
68 | html! { <Child ..blah /> };
|
||||||
| ^^^^ not found in this scope
|
| ^^^^ not found in this scope
|
||||||
|
|
||||||
error[E0609]: no field `r#type` on type `ChildProperties`
|
error[E0425]: cannot find value `props` in this scope
|
||||||
--> $DIR/component-fail.rs:60:20
|
--> $DIR/component-fail.rs:69:30
|
||||||
|
|
|
|
||||||
60 | html! { <Child type=0 /> };
|
69 | html! { <Child value=1 ..props /> };
|
||||||
|
| ^^^^^ not found in this scope
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/component-fail.rs:53:22
|
||||||
|
|
|
||||||
|
53 | html! { <Child ..p1 ..p2 /> };
|
||||||
|
| ----- ^^^^^^^ expected struct `ChildProperties`, found struct `std::ops::Range`
|
||||||
|
| |
|
||||||
|
| expected due to this
|
||||||
|
|
|
||||||
|
= note: expected struct `ChildProperties`
|
||||||
|
found struct `std::ops::Range<_>`
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/component-fail.rs:53:14
|
||||||
|
|
|
||||||
|
53 | html! { <Child ..p1 ..p2 /> };
|
||||||
|
| ^^^^^ expected struct `ChildProperties`, found struct `std::ops::Range`
|
||||||
|
|
|
||||||
|
= note: expected struct `ChildProperties`
|
||||||
|
found struct `std::ops::Range<_>`
|
||||||
|
|
||||||
|
error[E0609]: no field `value` on type `ChildProperties`
|
||||||
|
--> $DIR/component-fail.rs:69:20
|
||||||
|
|
|
||||||
|
69 | html! { <Child value=1 ..props /> };
|
||||||
|
| ^^^^^ unknown field
|
||||||
|
|
|
||||||
|
= note: available fields are: `string`, `int`
|
||||||
|
|
||||||
|
error[E0609]: no field `r#type` on type `ChildProperties`
|
||||||
|
--> $DIR/component-fail.rs:71:20
|
||||||
|
|
|
||||||
|
71 | html! { <Child type=0 /> };
|
||||||
| ^^^^ unknown field
|
| ^^^^ unknown field
|
||||||
|
|
|
|
||||||
= note: available fields are: `string`, `int`
|
= note: available fields are: `string`, `int`
|
||||||
|
|
||||||
error[E0599]: no method named `r#type` found for struct `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>` in the current scope
|
error[E0599]: no method named `r#type` found for struct `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>` in the current scope
|
||||||
--> $DIR/component-fail.rs:60:20
|
--> $DIR/component-fail.rs:71:20
|
||||||
|
|
|
|
||||||
4 | #[derive(Clone, Properties, PartialEq)]
|
4 | #[derive(Clone, Properties, PartialEq)]
|
||||||
| ---------- method `r#type` not found for this
|
| ---------- method `r#type` not found for this
|
||||||
...
|
...
|
||||||
60 | html! { <Child type=0 /> };
|
71 | html! { <Child type=0 /> };
|
||||||
| ^^^^ method not found in `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>`
|
| ^^^^ method not found in `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>`
|
||||||
|
|
||||||
error[E0609]: no field `unknown` on type `ChildProperties`
|
error[E0609]: no field `unknown` on type `ChildProperties`
|
||||||
--> $DIR/component-fail.rs:63:20
|
--> $DIR/component-fail.rs:74:20
|
||||||
|
|
|
|
||||||
63 | html! { <Child unknown="unknown" /> };
|
74 | html! { <Child unknown="unknown" /> };
|
||||||
| ^^^^^^^ unknown field
|
| ^^^^^^^ unknown field
|
||||||
|
|
|
|
||||||
= note: available fields are: `string`, `int`
|
= note: available fields are: `string`, `int`
|
||||||
|
|
||||||
error[E0599]: no method named `unknown` found for struct `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>` in the current scope
|
error[E0599]: no method named `unknown` found for struct `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>` in the current scope
|
||||||
--> $DIR/component-fail.rs:63:20
|
--> $DIR/component-fail.rs:74:20
|
||||||
|
|
|
|
||||||
4 | #[derive(Clone, Properties, PartialEq)]
|
4 | #[derive(Clone, Properties, PartialEq)]
|
||||||
| ---------- method `unknown` not found for this
|
| ---------- method `unknown` not found for this
|
||||||
...
|
...
|
||||||
63 | html! { <Child unknown="unknown" /> };
|
74 | html! { <Child unknown="unknown" /> };
|
||||||
| ^^^^^^^ method not found in `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>`
|
| ^^^^^^^ method not found in `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>`
|
||||||
|
|
||||||
error[E0277]: the trait bound `(): IntoPropValue<String>` is not satisfied
|
error[E0277]: the trait bound `(): IntoPropValue<String>` is not satisfied
|
||||||
--> $DIR/component-fail.rs:66:33
|
--> $DIR/component-fail.rs:77:33
|
||||||
|
|
|
|
||||||
66 | html! { <Child int=1 string={} /> };
|
77 | html! { <Child int=1 string={} /> };
|
||||||
| ^^ the trait `IntoPropValue<String>` is not implemented for `()`
|
| ^^ the trait `IntoPropValue<String>` is not implemented for `()`
|
||||||
|
|
||||||
error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfied
|
error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfied
|
||||||
--> $DIR/component-fail.rs:67:33
|
--> $DIR/component-fail.rs:78:33
|
||||||
|
|
|
|
||||||
67 | html! { <Child int=1 string=3 /> };
|
78 | html! { <Child int=1 string=3 /> };
|
||||||
| ^ the trait `IntoPropValue<String>` is not implemented for `{integer}`
|
| ^ the trait `IntoPropValue<String>` is not implemented for `{integer}`
|
||||||
|
|
|
|
||||||
= help: the following implementations were found:
|
= help: the following implementations were found:
|
||||||
@ -243,9 +349,9 @@ error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfie
|
|||||||
and 11 others
|
and 11 others
|
||||||
|
|
||||||
error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfied
|
error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfied
|
||||||
--> $DIR/component-fail.rs:68:34
|
--> $DIR/component-fail.rs:79:34
|
||||||
|
|
|
|
||||||
68 | html! { <Child int=1 string={3} /> };
|
79 | html! { <Child int=1 string={3} /> };
|
||||||
| ^ the trait `IntoPropValue<String>` is not implemented for `{integer}`
|
| ^ the trait `IntoPropValue<String>` is not implemented for `{integer}`
|
||||||
|
|
|
|
||||||
= help: the following implementations were found:
|
= help: the following implementations were found:
|
||||||
@ -256,91 +362,91 @@ error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfie
|
|||||||
and 11 others
|
and 11 others
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/component-fail.rs:69:31
|
--> $DIR/component-fail.rs:80:31
|
||||||
|
|
|
|
||||||
69 | html! { <Child int=1 ref={()} /> };
|
80 | html! { <Child int=1 ref={()} /> };
|
||||||
| ^^ expected struct `NodeRef`, found `()`
|
| ^^ expected struct `NodeRef`, found `()`
|
||||||
|
|
||||||
error[E0277]: the trait bound `u32: IntoPropValue<i32>` is not satisfied
|
error[E0277]: the trait bound `u32: IntoPropValue<i32>` is not satisfied
|
||||||
--> $DIR/component-fail.rs:71:24
|
--> $DIR/component-fail.rs:82:24
|
||||||
|
|
|
|
||||||
71 | html! { <Child int=0u32 /> };
|
82 | html! { <Child int=0u32 /> };
|
||||||
| ^^^^ the trait `IntoPropValue<i32>` is not implemented for `u32`
|
| ^^^^ the trait `IntoPropValue<i32>` is not implemented for `u32`
|
||||||
|
|
||||||
error[E0599]: no method named `string` found for struct `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>` in the current scope
|
error[E0599]: no method named `string` found for struct `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>` in the current scope
|
||||||
--> $DIR/component-fail.rs:72:20
|
--> $DIR/component-fail.rs:83:20
|
||||||
|
|
|
|
||||||
4 | #[derive(Clone, Properties, PartialEq)]
|
4 | #[derive(Clone, Properties, PartialEq)]
|
||||||
| ---------- method `string` not found for this
|
| ---------- method `string` not found for this
|
||||||
...
|
...
|
||||||
72 | html! { <Child string="abc" /> };
|
83 | html! { <Child string="abc" /> };
|
||||||
| ^^^^^^ method not found in `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>`
|
| ^^^^^^ method not found in `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>`
|
||||||
|
|
||||||
error[E0609]: no field `children` on type `ChildProperties`
|
error[E0609]: no field `children` on type `ChildProperties`
|
||||||
--> $DIR/component-fail.rs:76:14
|
--> $DIR/component-fail.rs:87:14
|
||||||
|
|
|
|
||||||
76 | html! { <Child>{ "Not allowed" }</Child> };
|
87 | html! { <Child>{ "Not allowed" }</Child> };
|
||||||
| ^^^^^ unknown field
|
| ^^^^^ unknown field
|
||||||
|
|
|
|
||||||
= note: available fields are: `string`, `int`
|
= note: available fields are: `string`, `int`
|
||||||
|
|
||||||
error[E0599]: no method named `children` found for struct `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>` in the current scope
|
error[E0599]: no method named `children` found for struct `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>` in the current scope
|
||||||
--> $DIR/component-fail.rs:76:14
|
--> $DIR/component-fail.rs:87:14
|
||||||
|
|
|
|
||||||
4 | #[derive(Clone, Properties, PartialEq)]
|
4 | #[derive(Clone, Properties, PartialEq)]
|
||||||
| ---------- method `children` not found for this
|
| ---------- method `children` not found for this
|
||||||
...
|
...
|
||||||
76 | html! { <Child>{ "Not allowed" }</Child> };
|
87 | html! { <Child>{ "Not allowed" }</Child> };
|
||||||
| ^^^^^ method not found in `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>`
|
| ^^^^^ method not found in `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>`
|
||||||
|
|
||||||
error[E0609]: no field `children` on type `ChildProperties`
|
error[E0609]: no field `children` on type `ChildProperties`
|
||||||
--> $DIR/component-fail.rs:80:10
|
--> $DIR/component-fail.rs:94:10
|
||||||
|
|
|
|
||||||
80 | <Child with ChildProperties { string: "hello".to_owned(), int: 5 }>
|
94 | <Child ..ChildProperties { string: "hello".to_owned(), int: 5 }>
|
||||||
| ^^^^^ unknown field
|
| ^^^^^ unknown field
|
||||||
|
|
|
|
||||||
= note: available fields are: `string`, `int`
|
= note: available fields are: `string`, `int`
|
||||||
|
|
||||||
error[E0599]: no method named `build` found for struct `ChildContainerPropertiesBuilder<ChildContainerPropertiesBuilderStep_missing_required_prop_children>` in the current scope
|
error[E0599]: no method named `build` found for struct `ChildContainerPropertiesBuilder<ChildContainerPropertiesBuilderStep_missing_required_prop_children>` in the current scope
|
||||||
--> $DIR/component-fail.rs:85:14
|
--> $DIR/component-fail.rs:99:14
|
||||||
|
|
|
|
||||||
24 | #[derive(Clone, Properties, PartialEq)]
|
24 | #[derive(Clone, Properties, PartialEq)]
|
||||||
| ---------- method `build` not found for this
|
| ---------- method `build` not found for this
|
||||||
...
|
...
|
||||||
85 | html! { <ChildContainer /> };
|
99 | html! { <ChildContainer /> };
|
||||||
| ^^^^^^^^^^^^^^ method not found in `ChildContainerPropertiesBuilder<ChildContainerPropertiesBuilderStep_missing_required_prop_children>`
|
| ^^^^^^^^^^^^^^ method not found in `ChildContainerPropertiesBuilder<ChildContainerPropertiesBuilderStep_missing_required_prop_children>`
|
||||||
|
|
||||||
error[E0599]: no method named `build` found for struct `ChildContainerPropertiesBuilder<ChildContainerPropertiesBuilderStep_missing_required_prop_children>` in the current scope
|
error[E0599]: no method named `build` found for struct `ChildContainerPropertiesBuilder<ChildContainerPropertiesBuilderStep_missing_required_prop_children>` in the current scope
|
||||||
--> $DIR/component-fail.rs:86:14
|
--> $DIR/component-fail.rs:100:14
|
||||||
|
|
|
|
||||||
24 | #[derive(Clone, Properties, PartialEq)]
|
24 | #[derive(Clone, Properties, PartialEq)]
|
||||||
| ---------- method `build` not found for this
|
| ---------- method `build` not found for this
|
||||||
...
|
...
|
||||||
86 | html! { <ChildContainer></ChildContainer> };
|
100 | html! { <ChildContainer></ChildContainer> };
|
||||||
| ^^^^^^^^^^^^^^ method not found in `ChildContainerPropertiesBuilder<ChildContainerPropertiesBuilderStep_missing_required_prop_children>`
|
| ^^^^^^^^^^^^^^ method not found in `ChildContainerPropertiesBuilder<ChildContainerPropertiesBuilderStep_missing_required_prop_children>`
|
||||||
|
|
||||||
error[E0277]: the trait bound `VChild<Child>: From<yew::virtual_dom::VText>` is not satisfied
|
error[E0277]: the trait bound `VChild<Child>: From<yew::virtual_dom::VText>` is not satisfied
|
||||||
--> $DIR/component-fail.rs:87:31
|
--> $DIR/component-fail.rs:101:31
|
||||||
|
|
|
|
||||||
87 | html! { <ChildContainer>{ "Not allowed" }</ChildContainer> };
|
101 | html! { <ChildContainer>{ "Not allowed" }</ChildContainer> };
|
||||||
| ^^^^^^^^^^^^^ the trait `From<yew::virtual_dom::VText>` is not implemented for `VChild<Child>`
|
| ^^^^^^^^^^^^^ the trait `From<yew::virtual_dom::VText>` is not implemented for `VChild<Child>`
|
||||||
|
|
|
|
||||||
= note: required because of the requirements on the impl of `Into<VChild<Child>>` for `yew::virtual_dom::VText`
|
= note: required because of the requirements on the impl of `Into<VChild<Child>>` for `yew::virtual_dom::VText`
|
||||||
= note: required by `into`
|
= note: required by `into`
|
||||||
|
|
||||||
error[E0277]: the trait bound `VChild<Child>: From<VNode>` is not satisfied
|
error[E0277]: the trait bound `VChild<Child>: From<VNode>` is not satisfied
|
||||||
--> $DIR/component-fail.rs:88:29
|
--> $DIR/component-fail.rs:102:29
|
||||||
|
|
|
|
||||||
88 | html! { <ChildContainer><></></ChildContainer> };
|
102 | html! { <ChildContainer><></></ChildContainer> };
|
||||||
| ^ the trait `From<VNode>` is not implemented for `VChild<Child>`
|
| ^ the trait `From<VNode>` is not implemented for `VChild<Child>`
|
||||||
|
|
|
|
||||||
= note: required because of the requirements on the impl of `Into<VChild<Child>>` for `VNode`
|
= note: required because of the requirements on the impl of `Into<VChild<Child>>` for `VNode`
|
||||||
= note: required by `into`
|
= note: required by `into`
|
||||||
|
|
||||||
error[E0277]: the trait bound `VChild<Child>: From<VNode>` is not satisfied
|
error[E0277]: the trait bound `VChild<Child>: From<VNode>` is not satisfied
|
||||||
--> $DIR/component-fail.rs:89:30
|
--> $DIR/component-fail.rs:103:30
|
||||||
|
|
|
|
||||||
89 | html! { <ChildContainer><other /></ChildContainer> };
|
103 | html! { <ChildContainer><other /></ChildContainer> };
|
||||||
| ^^^^^ the trait `From<VNode>` is not implemented for `VChild<Child>`
|
| ^^^^^ the trait `From<VNode>` is not implemented for `VChild<Child>`
|
||||||
|
|
|
|
||||||
= note: required because of the requirements on the impl of `Into<VChild<Child>>` for `VNode`
|
= note: required because of the requirements on the impl of `Into<VChild<Child>>` for `VNode`
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
#![no_implicit_prelude]
|
#![no_implicit_prelude]
|
||||||
|
|
||||||
#[derive(::std::clone::Clone, ::yew::Properties, ::std::default::Default, ::std::cmp::PartialEq)]
|
#[derive(
|
||||||
|
::std::clone::Clone, ::yew::Properties, ::std::default::Default, ::std::cmp::PartialEq,
|
||||||
|
)]
|
||||||
pub struct ContainerProperties {
|
pub struct ContainerProperties {
|
||||||
pub int: i32,
|
pub int: i32,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
@ -41,13 +43,19 @@ impl ::std::convert::From<::yew::virtual_dom::VChild<AltChild>> for ChildrenVari
|
|||||||
impl ::std::convert::Into<::yew::virtual_dom::VNode> for ChildrenVariants {
|
impl ::std::convert::Into<::yew::virtual_dom::VNode> for ChildrenVariants {
|
||||||
fn into(self) -> ::yew::virtual_dom::VNode {
|
fn into(self) -> ::yew::virtual_dom::VNode {
|
||||||
match self {
|
match self {
|
||||||
Self::Child(comp) => ::yew::virtual_dom::VNode::VComp(::std::convert::Into::<::yew::virtual_dom::VComp>::into(comp)),
|
Self::Child(comp) => ::yew::virtual_dom::VNode::VComp(::std::convert::Into::<
|
||||||
Self::AltChild(comp) => ::yew::virtual_dom::VNode::VComp(::std::convert::Into::<::yew::virtual_dom::VComp>::into(comp)),
|
::yew::virtual_dom::VComp,
|
||||||
|
>::into(comp)),
|
||||||
|
Self::AltChild(comp) => ::yew::virtual_dom::VNode::VComp(::std::convert::Into::<
|
||||||
|
::yew::virtual_dom::VComp,
|
||||||
|
>::into(comp)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(::std::clone::Clone, ::yew::Properties, ::std::default::Default, ::std::cmp::PartialEq)]
|
#[derive(
|
||||||
|
::std::clone::Clone, ::yew::Properties, ::std::default::Default, ::std::cmp::PartialEq,
|
||||||
|
)]
|
||||||
pub struct ChildProperties {
|
pub struct ChildProperties {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub string: ::std::string::String,
|
pub string: ::std::string::String,
|
||||||
@ -86,7 +94,9 @@ impl ::yew::Component for AltChild {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(::std::clone::Clone, ::yew::Properties, ::std::default::Default, ::std::cmp::PartialEq)]
|
#[derive(
|
||||||
|
::std::clone::Clone, ::yew::Properties, ::std::default::Default, ::std::cmp::PartialEq,
|
||||||
|
)]
|
||||||
pub struct ChildContainerProperties {
|
pub struct ChildContainerProperties {
|
||||||
pub int: i32,
|
pub int: i32,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
@ -125,9 +135,12 @@ fn compile_pass() {
|
|||||||
let node_ref = <::yew::NodeRef as ::std::default::Default>::default();
|
let node_ref = <::yew::NodeRef as ::std::default::Default>::default();
|
||||||
::yew::html! {
|
::yew::html! {
|
||||||
<>
|
<>
|
||||||
<Child with props />
|
<Child ..::std::clone::Clone::clone(&props) />
|
||||||
<Child ref={::std::clone::Clone::clone(&node_ref)} with ::yew::props!(Child::Properties { int: 5 }) />
|
<Child int={1} ..props />
|
||||||
<Child with <<Child as ::yew::Component>::Properties as ::std::default::Default>::default() ref={node_ref} />
|
<Child ref={::std::clone::Clone::clone(&node_ref)} int={2} ..::yew::props!(Child::Properties { int: 5 }) />
|
||||||
|
<Child int=3 ref={::std::clone::Clone::clone(&node_ref)} ..::yew::props!(Child::Properties { int: 5 }) />
|
||||||
|
<Child ref={::std::clone::Clone::clone(&node_ref)} ..::yew::props!(Child::Properties { int: 5 }) />
|
||||||
|
<Child ref={node_ref} ..<<Child as ::yew::Component>::Properties as ::std::default::Default>::default() />
|
||||||
</>
|
</>
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -182,15 +195,25 @@ fn compile_pass() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let props = <<Container as ::yew::Component>::Properties as ::std::default::Default>::default();
|
let props = <<Container as ::yew::Component>::Properties as ::std::default::Default>::default();
|
||||||
|
let child_props =
|
||||||
|
<<Child as ::yew::Component>::Properties as ::std::default::Default>::default();
|
||||||
::yew::html! {
|
::yew::html! {
|
||||||
<>
|
<>
|
||||||
<Container int=1 />
|
<Container int=1 />
|
||||||
<Container int=1></Container>
|
<Container int=1></Container>
|
||||||
|
|
||||||
<Container with props>
|
<Container ..::std::clone::Clone::clone(&props)>
|
||||||
<div>{ "hello world" }</div>
|
<div>{ "hello world" }</div>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
|
<Container int=1 ..::std::clone::Clone::clone(&props)>
|
||||||
|
<div>{ "hello world" }</div>
|
||||||
|
</Container>
|
||||||
|
|
||||||
|
<Container int=1 ..props>
|
||||||
|
<Child int=2 string="hello" ..child_props />
|
||||||
|
</Container>
|
||||||
|
|
||||||
<Container int=1>
|
<Container int=1>
|
||||||
<Child int=2 />
|
<Child int=2 />
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@ -236,9 +236,9 @@ pub use yew_macro::html_nested;
|
|||||||
/// let props = yew::props!(Model::Properties { id: 2, name: Cow::from("Lemmy") });
|
/// let props = yew::props!(Model::Properties { id: 2, name: Cow::from("Lemmy") });
|
||||||
/// # assert_eq!(props.id, 2);
|
/// # assert_eq!(props.id, 2);
|
||||||
///
|
///
|
||||||
/// // Use the `with props` syntax to create a component with the props.
|
/// // Use the Rust-like struct update syntax to create a component with the props.
|
||||||
/// html! {
|
/// html! {
|
||||||
/// <Model key=1 with props />
|
/// <Model key=1 ..props />
|
||||||
/// }
|
/// }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
|||||||
@ -296,7 +296,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<Comp with props />
|
<Comp ..props />
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,8 +316,8 @@ mod tests {
|
|||||||
check_key(html! { <Comp key={test_key.clone()} /> });
|
check_key(html! { <Comp key={test_key.clone()} /> });
|
||||||
check_key(html! { <Comp key={test_key.clone()} field_1=1 /> });
|
check_key(html! { <Comp key={test_key.clone()} field_1=1 /> });
|
||||||
check_key(html! { <Comp field_1=1 key={test_key.clone()} /> });
|
check_key(html! { <Comp field_1=1 key={test_key.clone()} /> });
|
||||||
check_key(html! { <Comp with props key={test_key.clone()} /> });
|
check_key(html! { <Comp key={test_key.clone()} ..props /> });
|
||||||
check_key(html! { <Comp key={test_key.clone()} with props_2 /> });
|
check_key(html! { <Comp key={test_key.clone()} ..props_2 /> });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -337,8 +337,8 @@ mod tests {
|
|||||||
check_node_ref(html! { <Comp ref={test_node_ref.clone()} /> });
|
check_node_ref(html! { <Comp ref={test_node_ref.clone()} /> });
|
||||||
check_node_ref(html! { <Comp ref={test_node_ref.clone()} field_1=1 /> });
|
check_node_ref(html! { <Comp ref={test_node_ref.clone()} field_1=1 /> });
|
||||||
check_node_ref(html! { <Comp field_1=1 ref={test_node_ref.clone()} /> });
|
check_node_ref(html! { <Comp field_1=1 ref={test_node_ref.clone()} /> });
|
||||||
check_node_ref(html! { <Comp with props ref={test_node_ref.clone()} /> });
|
check_node_ref(html! { <Comp ref={test_node_ref.clone()} ..props /> });
|
||||||
check_node_ref(html! { <Comp ref={test_node_ref.clone()} with props_2 /> });
|
check_node_ref(html! { <Comp ref={test_node_ref.clone()} ..props_2 /> });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -27,7 +27,7 @@ impl Component for MyComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Properties)]
|
#[derive(Clone, PartialEq, Properties)]
|
||||||
struct Props {
|
struct Props {
|
||||||
prop1: String,
|
prop1: String,
|
||||||
prop2: String,
|
prop2: String,
|
||||||
@ -71,7 +71,10 @@ html!{
|
|||||||
<MyComponentWithProps prop1="lorem" prop2="ipsum" />
|
<MyComponentWithProps prop1="lorem" prop2="ipsum" />
|
||||||
|
|
||||||
// With the whole set of props provided at once
|
// With the whole set of props provided at once
|
||||||
<MyComponentWithProps with props />
|
<MyComponentWithProps ..props.clone() />
|
||||||
|
|
||||||
|
// With Properties from a variable and specific values overridden
|
||||||
|
<MyComponentWithProps prop2="lorem" ..props />
|
||||||
</>
|
</>
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
@ -116,7 +119,10 @@ html! {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
When using the `with props` syntax, the children passed in the `html!` macro overwrite the ones already present in the props.
|
The `html!` macro allows you to pass a base expression with the `..props` syntax instead of specifying each property individually,
|
||||||
|
similar to Rust's [Functional Update Syntax](https://doc.rust-lang.org/stable/reference/expressions/struct-expr.html#functional-update-syntax).
|
||||||
|
This base expression must occur after any individual props are passed.
|
||||||
|
When passing a base props expression with a `children` field, the children passed in the `html!` macro overwrite the ones already present in the props.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use yew::{Children, Component, Context, html, Html, props, Properties};
|
use yew::{Children, Component, Context, html, Html, props, Properties};
|
||||||
@ -152,7 +158,7 @@ let props = yew::props!(Container::Properties {
|
|||||||
});
|
});
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<Container with props>
|
<Container ..props>
|
||||||
// props.children will be overwritten with this
|
// props.children will be overwritten with this
|
||||||
<span>{ "I am a child, as you can see" }</span>
|
<span>{ "I am a child, as you can see" }</span>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@ -16,7 +16,10 @@ html!{
|
|||||||
<MyComponent prop1="lorem" prop2="ipsum" />
|
<MyComponent prop1="lorem" prop2="ipsum" />
|
||||||
|
|
||||||
// With the whole set of props provided at once
|
// With the whole set of props provided at once
|
||||||
<MyComponent with props />
|
<MyComponent ..props />
|
||||||
|
|
||||||
|
// With Properties from a variable and specific values overridden
|
||||||
|
<MyComponent prop2="lorem" ..props />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@ -18,7 +18,7 @@ html!{
|
|||||||
<MyComponent prop1="lorem" prop2="ipsum" />
|
<MyComponent prop1="lorem" prop2="ipsum" />
|
||||||
|
|
||||||
// 同时提供全套的 props
|
// 同时提供全套的 props
|
||||||
<MyComponent with props />
|
<MyComponent ..props />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@ -18,7 +18,7 @@ html!{
|
|||||||
<MyComponent prop1="lorem" prop2="ipsum" />
|
<MyComponent prop1="lorem" prop2="ipsum" />
|
||||||
|
|
||||||
// 一次提供很多屬性
|
// 一次提供很多屬性
|
||||||
<MyComponent with props />
|
<MyComponent ..props />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user