Add requirement for braces around most props (#1939)

* Limit the properties to literals and brace-enclosed expressions

* Update examples with new syntax

* Update packages/yew-macro/src/props/prop.rs

Co-authored-by: Teymour Aldridge <42674621+teymour-aldridge@users.noreply.github.com>

* Fix lints and strip braces around single expressions

* Update docs with new prop syntax

* Add some test cases for new syntax

* Ensure all tests are passing

* Clean up missed code

* Update tests

* Update reverted docs

* Revert versioned docs

* Fix optional attributes paragraph

* Remove accidentally added files

* Remove accidentally added french docs

* Update packages/yew-macro/src/props/prop.rs

Co-authored-by: mc1098 <m.cripps1@uni.brighton.ac.uk>

* Fix forgotten braces and test cases

* Revert i18n old docs

* Revert translated docs

* Remove suggested fix in favour of more succinct error message

* Update errors after rebase

* Remove files accidentally added while rebasing

* Fix merge conflicts

Co-authored-by: Teymour Aldridge <42674621+teymour-aldridge@users.noreply.github.com>
Co-authored-by: mc1098 <m.cripps1@uni.brighton.ac.uk>
This commit is contained in:
Xavientois 2021-07-18 12:54:21 -04:00 committed by GitHub
parent d89f1ccc5c
commit 4a14d0f0a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
91 changed files with 551 additions and 610 deletions

View File

@ -130,7 +130,7 @@ impl Boid {
points.push_str(&format!("{:.2},{:.2} ", x, y));
}
html! { <polygon points=points fill=color /> }
html! { <polygon points={points} fill={color} /> }
}
}

View File

@ -73,7 +73,7 @@ impl Component for Model {
html! {
<>
<h1 class="title">{ "Boids" }</h1>
<Simulation settings=settings.clone() generation=generation paused=paused />
<Simulation settings={settings.clone()} generation={generation} paused={paused} />
{ self.view_panel() }
</>
}
@ -87,9 +87,9 @@ impl Model {
<div class="panel">
{ self.view_settings() }
<div class="panel__buttons">
<button onclick=link.callback(|_| Msg::TogglePause)>{ pause_text }</button>
<button onclick=link.callback(|_| Msg::ResetSettings)>{ "Use Defaults" }</button>
<button onclick=link.callback(|_| Msg::RestartSimulation)>{ "Restart" }</button>
<button onclick={link.callback(|_| Msg::TogglePause)}>{ pause_text }</button>
<button onclick={link.callback(|_| Msg::ResetSettings)}>{ "Use Defaults" }</button>
<button onclick={link.callback(|_| Msg::RestartSimulation)}>{ "Restart" }</button>
</div>
</div>
}
@ -119,48 +119,48 @@ impl Model {
<div class="settings">
<Slider label="Number of Boids"
min=1.0 max=600.0
onchange=settings_callback!(link, settings; boids as usize)
value=settings.boids as f64
onchange={settings_callback!(link, settings; boids as usize)}
value={settings.boids as f64}
/>
<Slider label="View Distance"
max=500.0 step=10.0
onchange=settings_callback!(link, settings; visible_range)
value=settings.visible_range
onchange={settings_callback!(link, settings; visible_range)}
value={settings.visible_range}
/>
<Slider label="Spacing"
max=100.0
onchange=settings_callback!(link, settings; min_distance)
value=settings.min_distance
onchange={settings_callback!(link, settings; min_distance)}
value={settings.min_distance}
/>
<Slider label="Max Speed"
max=50.0
onchange=settings_callback!(link, settings; max_speed)
value=settings.max_speed
onchange={settings_callback!(link, settings; max_speed)}
value={settings.max_speed}
/>
<Slider label="Cohesion"
max=0.5 percentage=true
onchange=settings_callback!(link, settings; cohesion_factor)
value=settings.cohesion_factor
onchange={settings_callback!(link, settings; cohesion_factor)}
value={settings.cohesion_factor}
/>
<Slider label="Separation"
max=1.0 percentage=true
onchange=settings_callback!(link, settings; separation_factor)
value=settings.separation_factor
onchange={settings_callback!(link, settings; separation_factor)}
value={settings.separation_factor}
/>
<Slider label="Alignment"
max=0.5 percentage=true
onchange=settings_callback!(link, settings; alignment_factor)
value=settings.alignment_factor
onchange={settings_callback!(link, settings; alignment_factor)}
value={settings.alignment_factor}
/>
<Slider label="Turn Speed"
max=1.5 percentage=true
onchange=settings_callback!(link, settings; turn_speed_ratio)
value=settings.turn_speed_ratio
onchange={settings_callback!(link, settings; turn_speed_ratio)}
value={settings.turn_speed_ratio}
/>
<Slider label="Color Adaption"
max=1.5 percentage=true
onchange=settings_callback!(link, settings; color_adapt_factor)
value=settings.color_adapt_factor
onchange={settings_callback!(link, settings; color_adapt_factor)}
value={settings.color_adapt_factor}
/>
</div>
}

View File

@ -104,7 +104,7 @@ impl Component for Simulation {
let view_box = format!("0 0 {} {}", SIZE.x, SIZE.y);
html! {
<svg class="simulation-window" viewBox=view_box>
<svg class="simulation-window" viewBox={view_box}>
{ for self.boids.iter().map(Boid::render) }
</svg>
}

View File

@ -80,13 +80,13 @@ impl Component for Slider {
html! {
<div class="slider">
<label for=id.clone() class="slider__label">{ label }</label>
<label for={id.clone()} class="slider__label">{ label }</label>
<input type="range"
id=id
id={id}
class="slider__input"
min=min.to_string() max=max.to_string() step=step.to_string()
oninput=onchange.reform(|data: InputData| data.value.parse().unwrap())
value=value.to_string()
min={min.to_string()} max={max.to_string()} step={step.to_string()}
oninput={onchange.reform(|data: InputData| data.value.parse().unwrap())}
value={value.to_string()}
/>
<span class="slider__value">{ display_value }</span>
</div>

View File

@ -45,17 +45,17 @@ impl Component for Model {
<div>
<div class="panel">
// A button to send the Increment message
<button class="button" onclick=self.link.callback(|_| Msg::Increment)>
<button class="button" onclick={self.link.callback(|_| Msg::Increment)}>
{ "+1" }
</button>
// A button to send the Decrement message
<button onclick=self.link.callback(|_| Msg::Decrement)>
<button onclick={self.link.callback(|_| Msg::Decrement)}>
{ "-1" }
</button>
// A button to send two Increment messages
<button onclick=self.link.batch_callback(|_| vec![Msg::Increment, Msg::Increment])>
<button onclick={self.link.batch_callback(|_| vec![Msg::Increment, Msg::Increment])}>
{ "+1, +1" }
</button>

View File

@ -76,32 +76,32 @@ impl Component for AddClientForm {
<>
<div class="names">
<input
class=classes!("new-client", "firstname")
class={classes!("new-client", "firstname")}
placeholder="First name"
value=client.first_name.clone()
oninput=link.callback(|e: InputData| Msg::UpdateFirstName(e.value))
value={client.first_name.clone()}
oninput={link.callback(|e: InputData| Msg::UpdateFirstName(e.value))}
/>
<input
class=classes!("new-client", "lastname")
class={classes!("new-client", "lastname")}
placeholder="Last name"
value=client.last_name.clone()
oninput=link.callback(|e: InputData| Msg::UpdateLastName(e.value))
value={client.last_name.clone()}
oninput={link.callback(|e: InputData| Msg::UpdateLastName(e.value))}
/>
<textarea
class=classes!("new-client", "description")
class={classes!("new-client", "description")}
placeholder="Description"
value=client.description.clone()
oninput=link.callback(|e: InputData| Msg::UpdateDescription(e.value))
value={client.description.clone()}
oninput={link.callback(|e: InputData| Msg::UpdateDescription(e.value))}
/>
</div>
<button
disabled=client.first_name.is_empty() || client.last_name.is_empty()
onclick=link.callback(|_| Msg::Add)
disabled={client.first_name.is_empty() || client.last_name.is_empty()}
onclick={link.callback(|_| Msg::Add)}
>
{ "Add New" }
</button>
<button onclick=link.callback(|_| Msg::Abort)>
<button onclick={link.callback(|_| Msg::Abort)}>
{ "Go Back" }
</button>
</>

View File

@ -97,21 +97,21 @@ impl Component for Model {
<div class="clients">
{ for self.clients.iter().map(Client::render) }
</div>
<button onclick=self.link.callback(|_| Msg::SwitchTo(Scene::NewClientForm))>{ "Add New" }</button>
<button onclick=self.link.callback(|_| Msg::SwitchTo(Scene::Settings))>{ "Settings" }</button>
<button onclick={self.link.callback(|_| Msg::SwitchTo(Scene::NewClientForm))}>{ "Add New" }</button>
<button onclick={self.link.callback(|_| Msg::SwitchTo(Scene::Settings))}>{ "Settings" }</button>
</div>
},
Scene::NewClientForm => html! {
<div class="crm">
<h1>{"Add a new client"}</h1>
<AddClientForm on_add=self.link.callback(Msg::AddClient) on_abort=self.link.callback(|_| Msg::SwitchTo(Scene::ClientsList)) />
<AddClientForm on_add={self.link.callback(Msg::AddClient)} on_abort={self.link.callback(|_| Msg::SwitchTo(Scene::ClientsList))} />
</div>
},
Scene::Settings => html! {
<div>
<h1>{"Settings"}</h1>
<button onclick=self.link.callback(|_| Msg::ClearClients)>{ "Remove all clients" }</button>
<button onclick=self.link.callback(|_| Msg::SwitchTo(Scene::ClientsList))>{ "Go Back" }</button>
<button onclick={self.link.callback(|_| Msg::ClearClients)}>{ "Remove all clients" }</button>
<button onclick={self.link.callback(|_| Msg::SwitchTo(Scene::ClientsList))}>{ "Go Back" }</button>
</div>
},
}

View File

@ -58,7 +58,7 @@ impl Component for CounterModel {
</p>
// Add button to send a destroy command to the parent app
<button class="destroy" onclick=Callback::from(move |_| destroy_callback.emit(()))>
<button class="destroy" onclick={Callback::from(move |_| destroy_callback.emit(()))}>
{ "Destroy this app" }
</button>
</>

View File

@ -98,13 +98,13 @@ impl Component for Model {
// Create button to create a new app
<button
class="create"
onclick=self.link.callback(|_| Msg::SpawnCounterAppInstance)
onclick={self.link.callback(|_| Msg::SpawnCounterAppInstance)}
>
{ "Spawn new CounterModel app" }
</button>
</div>
// Create a container for all the app instances
<div ref=self.apps_container_ref.clone()>
<div ref={self.apps_container_ref.clone()}>
</div>
</>
}

View File

@ -87,7 +87,7 @@ impl Component for Model {
<div>
<div>
<p>{ "Choose a file to upload to see the uploaded bytes" }</p>
<input type="file" multiple=true onchange=self.link.callback(move |value| {
<input type="file" multiple=true onchange={self.link.callback(move |value| {
let mut result = Vec::new();
if let ChangeData::Files(files) = value {
let files = js_sys::try_iter(&files)
@ -98,12 +98,12 @@ impl Component for Model {
result.extend(files);
}
Msg::Files(result, flag)
})
})}
/>
</div>
<div>
<label>{ "Read bytes" }</label>
<input type="checkbox" checked=flag onclick=self.link.callback(|_| Msg::ToggleReadBytes) />
<input type="checkbox" checked={flag} onclick={self.link.callback(|_| Msg::ToggleReadBytes)} />
</div>
<ul>
{ for self.files.iter().map(|f| Self::view_file(f)) }

View File

@ -118,10 +118,10 @@ impl Component for Model {
match &self.markdown {
FetchState::NotFetching => html! {
<>
<button onclick=self.link.callback(|_| Msg::GetMarkdown)>
<button onclick={self.link.callback(|_| Msg::GetMarkdown)}>
{ "Get Markdown" }
</button>
<button onclick=self.link.callback(|_| Msg::GetError)>
<button onclick={self.link.callback(|_| Msg::GetError)}>
{ "Get using incorrect URL" }
</button>
</>

View File

@ -95,8 +95,8 @@ impl Model {
}
};
html! {
<div key=idx class=classes!("game-cellule", cellule_status)
onclick=self.link.callback(move |_| Msg::ToggleCellule(idx))>
<div key={idx} class={classes!("game-cellule", cellule_status)}
onclick={self.link.callback(move |_| Msg::ToggleCellule(idx))}>
</div>
}
}
@ -180,7 +180,7 @@ impl Component for Model {
.enumerate()
.map(|(x, cell)| self.view_cellule(idx_offset + x, cell));
html! {
<div key=y class="game-row">
<div key={y} class="game-row">
{ for cells }
</div>
}
@ -198,11 +198,11 @@ impl Component for Model {
{ for cell_rows }
</div>
<div class="game-buttons">
<button class="game-button" onclick=self.link.callback(|_| Msg::Random)>{ "Random" }</button>
<button class="game-button" onclick=self.link.callback(|_| Msg::Step)>{ "Step" }</button>
<button class="game-button" onclick=self.link.callback(|_| Msg::Start)>{ "Start" }</button>
<button class="game-button" onclick=self.link.callback(|_| Msg::Stop)>{ "Stop" }</button>
<button class="game-button" onclick=self.link.callback(|_| Msg::Reset)>{ "Reset" }</button>
<button class="game-button" onclick={self.link.callback(|_| Msg::Random)}>{ "Random" }</button>
<button class="game-button" onclick={self.link.callback(|_| Msg::Step)}>{ "Step" }</button>
<button class="game-button" onclick={self.link.callback(|_| Msg::Start)}>{ "Start" }</button>
<button class="game-button" onclick={self.link.callback(|_| Msg::Stop)}>{ "Stop" }</button>
<button class="game-button" onclick={self.link.callback(|_| Msg::Reset)}>{ "Reset" }</button>
</div>
</section>
</section>

View File

@ -57,13 +57,13 @@ impl Component for Model {
<>
<textarea
class="code-block"
oninput=self.link.callback(|input: InputData| Msg::Payload(input.value))
value=self.payload.clone()
oninput={self.link.callback(|input: InputData| Msg::Payload(input.value))}
value={self.payload.clone()}
/>
<button onclick=self.link.callback(|_| Msg::Payload(bindings::get_payload()))>
<button onclick={self.link.callback(|_| Msg::Payload(bindings::get_payload()))}>
{ "Get the payload!" }
</button>
<button onclick=self.link.callback(|_| Msg::AsyncPayload) >
<button onclick={self.link.callback(|_| Msg::AsyncPayload)} >
{ "Get the payload later!" }
</button>
<p class="code-block">

View File

@ -147,7 +147,7 @@ impl Component for Model {
html! {
<div class="container">
<div class="row">
<p class="h2" ref=self.delta_ref.clone()/>
<p class="h2" ref={self.delta_ref.clone()}/>
<hr />
</div>
{ self.action_view() }
@ -169,8 +169,8 @@ impl Model {
{ self.build_component_ratio }
</p>
<input name="ratio" type="range" class="form-control-range" min="0.0" max="1.0" step="any"
value=self.build_component_ratio.to_string()
oninput=self.link.callback(|e: InputData| Msg::ChangeRatio(e.value))
value={self.build_component_ratio.to_string()}
oninput={self.link.callback(|e: InputData| Msg::ChangeRatio(e.value))}
/>
</div>
</div>
@ -182,74 +182,74 @@ impl Model {
<>
<div class="row">
<div class="col">
<button class="btn_size alert alert-danger" onclick=self.link.callback(|_| Msg::DeleteEverybody)>
<button class="btn_size alert alert-danger" onclick={self.link.callback(|_| Msg::DeleteEverybody)}>
{ "Delete everybody" }
</button>
</div>
<div class="col">
<button class="btn_size alert alert-success" onclick=self.link.callback(|_| Msg::CreatePersons(1))>
<button class="btn_size alert alert-success" onclick={self.link.callback(|_| Msg::CreatePersons(1))}>
{ "Create 1" }
</button>
</div>
<div class="col">
<button class="btn_size alert alert-success" onclick=self.link.callback(|_| Msg::CreatePersons(5))>
<button class="btn_size alert alert-success" onclick={self.link.callback(|_| Msg::CreatePersons(5))}>
{ "Create 5" }
</button>
</div>
<div class="col">
<button class="btn_size alert alert-success" onclick=self.link.callback(|_| Msg::CreatePersons(100))>
<button class="btn_size alert alert-success" onclick={self.link.callback(|_| Msg::CreatePersons(100))}>
{ "Create 100" }
</button>
</div>
<div class="col">
<button class="btn_size alert alert-success" onclick=self.link.callback(|_| Msg::CreatePersons(500))>
<button class="btn_size alert alert-success" onclick={self.link.callback(|_| Msg::CreatePersons(500))}>
{ "Create 500" }
</button>
</div>
<div class="col">
<button class="btn_size alert alert-success" onclick=self.link.callback(|_| Msg::CreatePersonsPrepend(1))>
<button class="btn_size alert alert-success" onclick={self.link.callback(|_| Msg::CreatePersonsPrepend(1))}>
{ "Prepend 1" }
</button>
</div>
<div class="col">
<button class="btn_size alert alert-success" onclick=self.link.callback(|_| Msg::CreatePersonsPrepend(5))>
<button class="btn_size alert alert-success" onclick={self.link.callback(|_| Msg::CreatePersonsPrepend(5))}>
{ "Prepend 5" }
</button>
</div>
</div>
<div class="row">
<div class="col">
<button class="btn_size alert alert-warning" onclick=self.link.callback(|_| Msg::ToggleKeyed)>
<button class="btn_size alert alert-warning" onclick={self.link.callback(|_| Msg::ToggleKeyed)}>
{ if self.keyed { "Disable keys" } else { "Enable keys" } }
</button>
</div>
<div class="col">
<button class="btn_size alert alert-info" onclick=self.link.callback(|_| Msg::SwapRandom)>
<button class="btn_size alert alert-info" onclick={self.link.callback(|_| Msg::SwapRandom)}>
{ "Swap random" }
</button>
</div>
<div class="col">
<button class="btn_size alert alert-info" onclick=self.link.callback(|_| Msg::ReverseList)>
<button class="btn_size alert alert-info" onclick={self.link.callback(|_| Msg::ReverseList)}>
{ "Reverse list" }
</button>
</div>
<div class="col">
<button class="btn_size alert alert-info" onclick=self.link.callback(|_| Msg::SortById)>
<button class="btn_size alert alert-info" onclick={self.link.callback(|_| Msg::SortById)}>
{ "Sort by id" }
</button>
</div>
<div class="col">
<button class="btn_size alert alert-info" onclick=self.link.callback(|_| Msg::SortByName)>
<button class="btn_size alert alert-info" onclick={self.link.callback(|_| Msg::SortByName)}>
{ "Sort by name" }
</button>
</div>
<div class="col">
<button class="btn_size alert alert-info" onclick=self.link.callback(|_| Msg::SortByAge)>
<button class="btn_size alert alert-info" onclick={self.link.callback(|_| Msg::SortByAge)}>
{ "Sort by age" }
</button>
</div>
<div class="col">
<button class="btn_size alert alert-info" onclick=self.link.callback(|_| Msg::SortByAddress)>
<button class="btn_size alert alert-info" onclick={self.link.callback(|_| Msg::SortByAddress)}>
{ "Sort by address" }
</button>
</div>

View File

@ -69,7 +69,7 @@ impl Component for PersonComponent {
fn view(&self) -> Html {
html! {
<div class="text-info" id=self.info.id.to_string()>
<div class="text-info" id={self.info.id.to_string()}>
{ self.info.render() }
</div>
}
@ -102,13 +102,13 @@ impl PersonType {
Self::Inline(info) => {
if keyed {
html! {
<div key=info.id.to_string() class="text-danger" id=info.id.to_string()>
<div key={info.id.to_string()} class="text-danger" id={info.id.to_string()}>
{ info.render() }
</div>
}
} else {
html! {
<div class="text-danger" id=info.id.to_string()>
<div class="text-danger" id={info.id.to_string()}>
{ info.render() }
</div>
}
@ -116,9 +116,9 @@ impl PersonType {
}
Self::Component(info) => {
if keyed {
html! { <PersonComponent key=info.id.to_string() info=info.clone() /> }
html! { <PersonComponent key={info.id.to_string()} info={info.clone()} /> }
} else {
html! { <PersonComponent info=info.clone() /> }
html! { <PersonComponent info={info.clone()} /> }
}
}
}

View File

@ -39,8 +39,8 @@ impl Component for Model {
html! {
<div>
<input
value=self.name.clone()
oninput=self.link.callback(|e: InputData| Msg::UpdateName(e.value))
value={self.name.clone()}
oninput={self.link.callback(|e: InputData| Msg::UpdateName(e.value))}
/>
<p>{ self.name.chars().rev().collect::<String>() }</p>
</div>

View File

@ -76,9 +76,9 @@ impl Component for Model {
html! {
<div>
<nav class="menu">
<button onclick=self.link.callback(|_| Msg::SendToWorker)>{ "Send to Thread" }</button>
<button onclick=self.link.callback(|_| Msg::SendToJob)>{ "Send to Job" }</button>
<button onclick=self.link.callback(|_| Msg::SendToContext)>{ "Send to Context" }</button>
<button onclick={self.link.callback(|_| Msg::SendToWorker)}>{ "Send to Thread" }</button>
<button onclick={self.link.callback(|_| Msg::SendToJob)}>{ "Send to Job" }</button>
<button onclick={self.link.callback(|_| Msg::SendToContext)}>{ "Send to Context" }</button>
</nav>
</div>
}

View File

@ -48,21 +48,22 @@ impl Component for App {
let sub_list_link = &self.sub_list_link;
// note the use of `html_nested!` instead of `html!`.
let letters = ('A'..='C')
.map(|letter| html_nested! { <ListItem name=letter.to_string() on_hover=on_hover /> });
let letters = ('A'..='C').map(
|letter| html_nested! { <ListItem name={letter.to_string()} on_hover={on_hover} /> },
);
html! {
<div class="main" onmouseenter=onmouseenter>
<div class="main" onmouseenter={onmouseenter}>
<h1>{ "Nested List Demo" }</h1>
<List on_hover=on_hover weak_link=list_link>
<ListHeader text="Calling all Rusties!" on_hover=on_hover list_link=list_link />
<ListItem name="Rustin" on_hover=on_hover />
<ListItem hide=true name="Rustaroo" on_hover=on_hover />
<ListItem name="Rustifer" on_hover=on_hover>
<List on_hover={on_hover} weak_link={list_link}>
<ListHeader text="Calling all Rusties!" on_hover={on_hover} list_link={list_link} />
<ListItem name="Rustin" on_hover={on_hover} />
<ListItem hide=true name="Rustaroo" on_hover={on_hover} />
<ListItem name="Rustifer" on_hover={on_hover}>
<div class="sublist">{ "Sublist!" }</div>
<List on_hover=on_hover weak_link=sub_list_link>
<ListHeader text="Sub Rusties!" on_hover=on_hover list_link=sub_list_link/>
<ListItem hide=true name="Hidden Sub" on_hover=on_hover />
<List on_hover={on_hover} weak_link={sub_list_link}>
<ListHeader text="Sub Rusties!" on_hover={on_hover} list_link={sub_list_link}/>
<ListItem hide=true name="Hidden Sub" on_hover={on_hover} />
{ for letters }
</List>
</ListItem>

View File

@ -44,8 +44,8 @@ impl Component for ListHeader {
html! {
<div
class="list-header"
onmouseover=onmouseover
onclick=list_link.callback(|_| ListMsg::HeaderClick)
onmouseover={onmouseover}
onclick={list_link.callback(|_| ListMsg::HeaderClick)}
>
{ &self.props.text }
</div>

View File

@ -45,7 +45,7 @@ impl Component for ListItem {
})
};
html! {
<div class="list-item" onmouseover=onmouseover>
<div class="list-item" onmouseover={onmouseover}>
{ &self.props.name }
{ self.view_details() }
</div>

View File

@ -102,8 +102,8 @@ impl Component for List {
let onmouseover = self.props.on_hover.reform(|_| Hovered::List);
let onmouseout = self.props.on_hover.reform(|_| Hovered::None);
html! {
<div class="list-container" onmouseout=onmouseout onmouseover=onmouseover>
<div class=classes!("list", inactive)>
<div class="list-container" onmouseout={onmouseout} onmouseover={onmouseover}>
<div class={classes!("list", inactive)}>
{ self.view_header() }
<div class="items">
{ self.view_items() }

View File

@ -41,7 +41,7 @@ impl Component for InputComponent {
<input
type="text"
class="input-component"
onmouseover=self.link.callback(|_| Msg::Hover)
onmouseover={self.link.callback(|_| Msg::Hover)}
/>
}
}

View File

@ -65,16 +65,16 @@ impl Component for Model {
<label>{ "Using tag ref: " }</label>
<input
type="text"
ref=self.refs[0].clone()
ref={self.refs[0].clone()}
class="input-element"
onmouseover=self.link.callback(|_| Msg::HoverIndex(0))
onmouseover={self.link.callback(|_| Msg::HoverIndex(0))}
/>
</div>
<div>
<label>{ "Using component ref: " }</label>
<InputComponent
ref=self.refs[1].clone()
on_hover=self.link.callback(|_| Msg::HoverIndex(1))
ref={self.refs[1].clone()}
on_hover={self.link.callback(|_| Msg::HoverIndex(1))}
/>
</div>
</div>

View File

@ -38,7 +38,7 @@ impl Component for Producer {
fn view(&self) -> Html {
html! {
<button onclick=self.link.callback(|_| Msg::Clicked)>
<button onclick={self.link.callback(|_| Msg::Clicked)}>
{ "PRESS ME" }
</button>
}

View File

@ -41,7 +41,7 @@ impl Component for AuthorCard {
<div class="media">
<div class="media-left">
<figure class="image is-128x128">
<img src=author.image_url.clone() />
<img src={author.image_url.clone()} />
</figure>
</div>
<div class="media-content">
@ -54,7 +54,7 @@ impl Component for AuthorCard {
</div>
</div>
<footer class="card-footer">
<Link<Route> classes=classes!("card-footer-item") route=Route::Author { id: author.seed }>
<Link<Route> classes={classes!("card-footer-item")} route={Route::Author { id: author.seed }}>
{ "Profile" }
</Link<Route>>
</footer>

View File

@ -53,7 +53,7 @@ impl Pagination {
html! {
<li>
<a class=classes!("pagination-link", is_current_class) aria-label=format!("Goto page {}", to_page) onclick=onclick>
<a class={classes!("pagination-link", is_current_class)} aria-label={format!("Goto page {}", to_page)} onclick={onclick}>
{ to_page }
</a>
</li>
@ -114,14 +114,14 @@ impl Pagination {
html! {
<>
<a class="pagination-previous"
disabled=page==1
onclick=on_switch_page.reform(move |_| page - 1)
disabled={page==1}
onclick={on_switch_page.reform(move |_| page - 1)}
>
{ "Previous" }
</a>
<a class="pagination-next"
disabled=page==total_pages
onclick=on_switch_page.reform(move |_| page + 1)
disabled={page==total_pages}
onclick={on_switch_page.reform(move |_| page + 1)}
>
{ "Next page" }
</a>

View File

@ -39,14 +39,14 @@ impl Component for PostCard {
<div class="card">
<div class="card-image">
<figure class="image is-2by1">
<img src=post.image_url.clone() loading="lazy" />
<img src={post.image_url.clone()} loading="lazy" />
</figure>
</div>
<div class="card-content">
<Link<Route> classes=classes!("title", "is-block") route=Route::Post { id: post.seed }>
<Link<Route> classes={classes!("title", "is-block")} route={Route::Post { id: post.seed }}>
{ &post.title }
</Link<Route>>
<Link<Route> classes=classes!("subtitle", "is-block") route=Route::Author { id: post.author.seed }>
<Link<Route> classes={classes!("subtitle", "is-block")} route={Route::Author { id: post.author.seed }}>
{ &post.author.name }
</Link<Route>>
</div>

View File

@ -64,7 +64,7 @@ impl Component for ProgressDelay {
fn view(&self) -> Html {
let value = self.value;
html! {
<progress class="progress is-primary" value=value.to_string() max=1.0>
<progress class="progress is-primary" value={value.to_string()} max=1.0>
{ format!("{:.0}%", 100.0 * value) }
</progress>
}

View File

@ -65,7 +65,7 @@ impl Component for Model {
{ self.view_nav() }
<main>
<Router<Route> render=Router::render(switch) />
<Router<Route> render={Router::render(switch)} />
</main>
<footer class="footer">
<div class="content has-text-centered">
@ -97,21 +97,21 @@ impl Model {
<h1 class="navbar-item is-size-3">{ "Yew Blog" }</h1>
<a role="button"
class=classes!("navbar-burger", "burger", active_class)
class={classes!("navbar-burger", "burger", active_class)}
aria-label="menu" aria-expanded="false"
onclick=link.callback(|_| Msg::ToggleNavbar)
onclick={link.callback(|_| Msg::ToggleNavbar)}
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div class=classes!("navbar-menu", active_class)>
<div class={classes!("navbar-menu", active_class)}>
<div class="navbar-start">
<Link<Route> classes=classes!("navbar-item") route=Route::Home>
<Link<Route> classes={classes!("navbar-item")} route={Route::Home}>
{ "Home" }
</Link<Route>>
<Link<Route> classes=classes!("navbar-item") route=Route::Posts>
<Link<Route> classes={classes!("navbar-item")} route={Route::Posts}>
{ "Posts" }
</Link<Route>>
@ -121,7 +121,7 @@ impl Model {
</a>
<div class="navbar-dropdown">
<a class="navbar-item">
<Link<Route> classes=classes!("navbar-item") route=Route::Authors>
<Link<Route> classes={classes!("navbar-item")} route={Route::Authors}>
{ "Meet the authors" }
</Link<Route>>
</a>
@ -137,13 +137,13 @@ impl Model {
fn switch(routes: &Route) -> Html {
match routes {
Route::Post { id } => {
html! { <Post seed=*id /> }
html! { <Post seed={*id} /> }
}
Route::Posts => {
html! { <PostList /> }
}
Route::Author { id } => {
html! { <Author seed=*id /> }
html! { <Author seed={*id} /> }
}
Route::Authors => {
html! { <AuthorList /> }

View File

@ -54,7 +54,7 @@ impl Component for Author {
</div>
<div class="tile is-parent">
<figure class="tile is-child image is-square">
<img src=author.image_url.clone() />
<img src={author.image_url.clone()} />
</figure>
</div>
<div class="tile is-parent">

View File

@ -42,7 +42,7 @@ impl Component for AuthorList {
html! {
<div class="tile is-parent">
<div class="tile is-child">
<AuthorCard seed=seed />
<AuthorCard seed={seed} />
</div>
</div>
}
@ -71,7 +71,7 @@ impl Component for AuthorList {
<div class="tile is-ancestor">
{ for authors }
</div>
<ProgressDelay duration_ms=CAROUSEL_DELAY_MS on_complete=self.link.callback(|_| Msg::NextAuthors) />
<ProgressDelay duration_ms={CAROUSEL_DELAY_MS} on_complete={self.link.callback(|_| Msg::NextAuthors)} />
</div>
</div>
}

View File

@ -47,7 +47,7 @@ impl Component for Post {
html! {
<>
<section class="hero is-medium is-light has-background">
<img class="hero-background is-transparent" src=Cow::Owned(post.meta.image_url.clone()) />
<img class="hero-background is-transparent" src={Cow::Owned(post.meta.image_url.clone())} />
<div class="hero-body">
<div class="container">
<h1 class="title">
@ -55,7 +55,7 @@ impl Component for Post {
</h1>
<h2 class="subtitle">
{ "by " }
<Link<Route> classes=classes!("has-text-weight-semibold") route=Route::Author { id: post.meta.author.seed }>
<Link<Route> classes={classes!("has-text-weight-semibold")} route={Route::Author { id: post.meta.author.seed }}>
{ &post.meta.author.name }
</Link<Route>>
</h2>
@ -78,12 +78,12 @@ impl Post {
<article class="media block box my-6">
<figure class="media-left">
<p class="image is-64x64">
<img src=Cow::Owned(quote.author.image_url.clone()) loading="lazy" />
<img src={Cow::Owned(quote.author.image_url.clone())} loading="lazy" />
</p>
</figure>
<div class="media-content">
<div class="content">
<Link<Route> classes=classes!("is-size-5") route=Route::Author { id: quote.author.seed }>
<Link<Route> classes={classes!("is-size-5")} route={Route::Author { id: quote.author.seed }}>
<strong>{ &quote.author.name }</strong>
</Link<Route>>
<p class="is-family-secondary">
@ -98,7 +98,7 @@ impl Post {
fn render_section_hero(&self, section: &content::Section) -> Html {
html! {
<section class="hero is-dark has-background mt-6 mb-3">
<img class="hero-background is-transparent" src=Cow::Owned(section.image_url.clone()) loading="lazy" />
<img class="hero-background is-transparent" src={Cow::Owned(section.image_url.clone())} loading="lazy" />
<div class="hero-body">
<div class="container">
<h2 class="subtitle">{ &section.title }</h2>

View File

@ -48,9 +48,9 @@ impl Component for PostList {
<h2 class="subtitle">{ "All of our quality writing in one place" }</h2>
{ self.view_posts() }
<Pagination
page=page
total_pages=TOTAL_PAGES
on_switch_page=self.link.callback(Msg::ShowPage)
page={page}
total_pages={TOTAL_PAGES}
on_switch_page={self.link.callback(Msg::ShowPage)}
/>
</div>
}
@ -62,7 +62,7 @@ impl PostList {
let mut cards = (0..ITEMS_PER_PAGE).map(|seed_offset| {
html! {
<li class="list-item mb-5">
<PostCard seed=start_seed + seed_offset />
<PostCard seed={start_seed + seed_offset} />
</li>
}
});

View File

@ -64,10 +64,10 @@ impl Component for Model {
fn view(&self) -> Html {
html! {
<>
<TextInput value="New post" onsubmit=self.link.callback(Msg::CreatePost) />
<TextInput value="New post" onsubmit={self.link.callback(Msg::CreatePost)} />
<div>
{ for self.post_ids.iter().map(|&id| html!{ <Post key=id id=id /> }) }
{ for self.post_ids.iter().map(|&id| html!{ <Post key={id} id={id} /> }) }
</div>
</>
}

View File

@ -72,8 +72,8 @@ impl Component for Post {
<h2>{ format!("Post #{}", self.id) }</h2>
<p>{text}</p>
<TextInput value=text.to_owned() onsubmit=self.link.callback(Msg::UpdateText) />
<button onclick=self.link.callback(|_| Msg::Delete)>
<TextInput value={text.to_owned()} onsubmit={self.link.callback(Msg::UpdateText)} />
<button onclick={self.link.callback(|_| Msg::Delete)}>
{ "Delete" }
</button>
</div>

View File

@ -57,12 +57,12 @@ impl Component for TextInput {
html! {
<input
type="text"
value=self.text.clone()
oninput=self.link.callback(|e: InputData| Msg::SetText(e.value))
onkeydown=self.link.batch_callback(move |e: KeyboardEvent| {
value={self.text.clone()}
oninput={self.link.callback(|e: InputData| Msg::SetText(e.value))}
onkeydown={self.link.batch_callback(move |e: KeyboardEvent| {
e.stop_propagation();
if e.key() == "Enter" { Some(Msg::Submit) } else { None }
})
})}
/>
}
}

View File

@ -131,13 +131,13 @@ impl Component for Model {
html! {
<>
<div id="buttons">
<button disabled=has_job onclick=self.link.callback(|_| Msg::StartTimeout)>
<button disabled={has_job} onclick={self.link.callback(|_| Msg::StartTimeout)}>
{ "Start Timeout" }
</button>
<button disabled=has_job onclick=self.link.callback(|_| Msg::StartInterval)>
<button disabled={has_job} onclick={self.link.callback(|_| Msg::StartInterval)}>
{ "Start Interval" }
</button>
<button disabled=!has_job onclick=self.link.callback(|_| Msg::Cancel)>
<button disabled={!has_job} onclick={self.link.callback(|_| Msg::Cancel)}>
{ "Cancel!" }
</button>
</div>

View File

@ -124,20 +124,20 @@ impl Component for Model {
<h1>{ "todos" }</h1>
{ self.view_input() }
</header>
<section class=classes!("main", hidden_class)>
<section class={classes!("main", hidden_class)}>
<input
type="checkbox"
class="toggle-all"
id="toggle-all"
checked=self.state.is_all_completed()
onclick=self.link.callback(|_| Msg::ToggleAll)
checked={self.state.is_all_completed()}
onclick={self.link.callback(|_| Msg::ToggleAll)}
/>
<label for="toggle-all" />
<ul class="todo-list">
{ for self.state.entries.iter().filter(|e| self.state.filter.fits(e)).enumerate().map(|e| self.view_entry(e)) }
</ul>
</section>
<footer class=classes!("footer", hidden_class)>
<footer class={classes!("footer", hidden_class)}>
<span class="todo-count">
<strong>{ self.state.total() }</strong>
{ " item(s) left" }
@ -145,7 +145,7 @@ impl Component for Model {
<ul class="filters">
{ for Filter::iter().map(|flt| self.view_filter(flt)) }
</ul>
<button class="clear-completed" onclick=self.link.callback(|_| Msg::ClearCompleted)>
<button class="clear-completed" onclick={self.link.callback(|_| Msg::ClearCompleted)}>
{ format!("Clear completed ({})", self.state.total_completed()) }
</button>
</footer>
@ -169,9 +169,9 @@ impl Model {
};
html! {
<li>
<a class=cls
href=filter.as_href()
onclick=self.link.callback(move |_| Msg::SetFilter(filter))
<a class={cls}
href={filter.as_href()}
onclick={self.link.callback(move |_| Msg::SetFilter(filter))}
>
{ filter }
</a>
@ -186,11 +186,11 @@ impl Model {
<input
class="new-todo"
placeholder="What needs to be done?"
value=self.state.value.clone()
oninput=self.link.callback(|e: InputData| Msg::Update(e.value))
onkeypress=self.link.batch_callback(|e: KeyboardEvent| {
value={self.state.value.clone()}
oninput={self.link.callback(|e: InputData| Msg::Update(e.value))}
onkeypress={self.link.batch_callback(|e: KeyboardEvent| {
if e.key() == "Enter" { Some(Msg::Add) } else { None }
})
})}
/>
/* Or multiline:
<ul>
@ -209,16 +209,16 @@ impl Model {
class.push(" completed");
}
html! {
<li class=class>
<li class={class}>
<div class="view">
<input
type="checkbox"
class="toggle"
checked=entry.completed
onclick=self.link.callback(move |_| Msg::Toggle(idx))
checked={entry.completed}
onclick={self.link.callback(move |_| Msg::Toggle(idx))}
/>
<label ondblclick=self.link.callback(move |_| Msg::ToggleEdit(idx))>{ &entry.description }</label>
<button class="destroy" onclick=self.link.callback(move |_| Msg::Remove(idx)) />
<label ondblclick={self.link.callback(move |_| Msg::ToggleEdit(idx))}>{ &entry.description }</label>
<button class="destroy" onclick={self.link.callback(move |_| Msg::Remove(idx))} />
</div>
{ self.view_entry_edit_input((idx, &entry)) }
</li>
@ -231,14 +231,14 @@ impl Model {
<input
class="edit"
type="text"
ref=self.focus_ref.clone()
value=self.state.edit_value.clone()
onmouseover=self.link.callback(|_| Msg::Focus)
oninput=self.link.callback(|e: InputData| Msg::UpdateEdit(e.value))
onblur=self.link.callback(move |_| Msg::Edit(idx))
onkeypress=self.link.batch_callback(move |e: KeyboardEvent| {
ref={self.focus_ref.clone()}
value={self.state.edit_value.clone()}
onmouseover={self.link.callback(|_| Msg::Focus)}
oninput={self.link.callback(|e: InputData| Msg::UpdateEdit(e.value))}
onblur={self.link.callback(move |_| Msg::Edit(idx))}
onkeypress={self.link.batch_callback(move |e: KeyboardEvent| {
if e.key() == "Enter" { Some(Msg::Edit(idx)) } else { None }
})
})}
/>
}
} else {

View File

@ -65,10 +65,10 @@ impl Component for Model {
html! {
<div>
<h3>{ format!("{} received <{}>", self.selector, self.title) }</h3>
<button onclick=self.link.callback(|_| Msg::SendToOpposite("One".into()))>{ "One" }</button>
<button onclick=self.link.callback(|_| Msg::SendToOpposite("Two".into()))>{ "Two" }</button>
<button onclick=self.link.callback(|_| Msg::SendToOpposite("Three".into()))>{ "Three" }</button>
<button onclick=self.link.callback(|_| Msg::SendToOpposite("Ping".into()))>{ "Ping" }</button>
<button onclick={self.link.callback(|_| Msg::SendToOpposite("One".into()))}>{ "One" }</button>
<button onclick={self.link.callback(|_| Msg::SendToOpposite("Two".into()))}>{ "Two" }</button>
<button onclick={self.link.callback(|_| Msg::SendToOpposite("Three".into()))}>{ "Three" }</button>
<button onclick={self.link.callback(|_| Msg::SendToOpposite("Ping".into()))}>{ "Ping" }</button>
</div>
}
}

View File

@ -76,7 +76,7 @@ impl Component for Model {
fn view(&self) -> Html {
html! {
<canvas ref=self.node_ref.clone() />
<canvas ref={self.node_ref.clone()} />
}
}

View File

@ -37,7 +37,7 @@
//!
//! html! {
//! <div>
//! <button onclick=self.link.callback(|_| Msg::Submit)>
//! <button onclick={self.link.callback(|_| Msg::Submit)}>
//! { "Submit" }
//! </button>
//! <>

View File

@ -8,7 +8,7 @@ use std::{
};
use syn::{
parse::{Parse, ParseStream},
Expr, Token,
Block, Expr, ExprBlock, Stmt, Token,
};
pub enum PropPunct {
@ -45,7 +45,7 @@ impl Parse for Prop {
"expected an expression following this equals sign",
));
}
let value = input.parse::<Expr>()?;
let value = strip_braces(input.parse::<Expr>()?)?;
Ok(Self {
label,
punct: Some(PropPunct::Eq(equals)),
@ -54,6 +54,30 @@ impl Parse for Prop {
}
}
fn strip_braces(expr: Expr) -> syn::Result<Expr> {
match expr {
Expr::Block(ExprBlock { block: Block { mut stmts, .. }, .. }) if stmts.len() == 1 => {
let stmt = stmts.remove(0);
match stmt {
Stmt::Expr(expr) => Ok(expr),
Stmt::Semi(_expr, semi) => Err(syn::Error::new_spanned(
semi,
"only an expression may be assigned as a property. Consider removing this semicolon",
)),
_ => Err(syn::Error::new_spanned(
stmt,
"only an expression may be assigned as a property",
))
}
}
Expr::Lit(_) | Expr::Block(_) => 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.".to_string(),
)),
}
}
/// List of props sorted in alphabetical order*.
///
/// \*The "children" prop always comes last to match the behaviour of the `Properties` derive macro.

View File

@ -59,17 +59,18 @@ fn compile_fail() {
html! { <Child with props > };
let (p1, p2);
html! { <Child with p1 with p2 /> };
html! { <Child with props ref=() ref=() /> };
html! { <Child with props ref=() ref=() value=1 /> };
html! { <Child with props ref=() value=1 ref=() /> };
html! { <Child with props value=1 ref=() ref=() /> };
html! { <Child value=1 with props ref=() ref=() /> };
html! { <Child value=1 ref=() with props ref=() /> };
html! { <Child ref=() ref=() value=1 with props /> };
html! { <Child with props ref={()} ref={()} /> };
html! { <Child with props ref={()} ref={()} value=1 /> };
html! { <Child with props ref={()} value=1 ref={()} /> };
html! { <Child with props value=1 ref={()} ref={()} /> };
html! { <Child value=1 with props ref={()} ref={()} /> };
html! { <Child value=1 ref={()} with props ref={()} /> };
html! { <Child ref={()} ref={()} value=1 with props /> };
html! { <Child with blah /> };
html! { <Child value=1 with props /> };
html! { <Child with props value=1 /> };
html! { <Child type=0 /> };
html! { <Child ref=() /> };
html! { <Child invalid-prop-name=0 /> };
html! { <Child unknown="unknown" /> };
html! { <Child string= /> };
@ -77,8 +78,8 @@ fn compile_fail() {
html! { <Child int=1 string={} /> };
html! { <Child int=1 string=3 /> };
html! { <Child int=1 string={3} /> };
html! { <Child int=1 ref=() /> };
html! { <Child int=1 ref=() ref=() /> };
html! { <Child int=1 ref={()} /> };
html! { <Child int=1 ref={()} ref={()} /> };
html! { <Child int=0u32 /> };
html! { <Child string="abc" /> };
html! { </Child> };
@ -102,7 +103,7 @@ fn compile_fail() {
// using `children` as a prop while simultaneously passing children using the syntactic sugar
let children = ChildrenRenderer::new(vec![html_nested! { <Child int=0 /> }]);
html! {
<ChildContainer children=children>
<ChildContainer children={children}>
<Child int=1 />
</ChildContainer>
};

View File

@ -35,46 +35,46 @@ error: there are two `with <props>` definitions for this component (note: you ca
| ^^^^^^^
error: `ref` can only be set once
--> $DIR/component-fail.rs:62:38
--> $DIR/component-fail.rs:62:40
|
62 | html! { <Child with props ref=() ref=() /> };
| ^^^
62 | html! { <Child with props ref={()} ref={()} /> };
| ^^^
error: `ref` can only be set once
--> $DIR/component-fail.rs:63:38
--> $DIR/component-fail.rs:63:40
|
63 | html! { <Child with props ref=() ref=() value=1 /> };
| ^^^
63 | 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:64:38
--> $DIR/component-fail.rs:64:40
|
64 | html! { <Child with props ref=() value=1 ref=() /> };
| ^^^^^
64 | 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`)
--> $DIR/component-fail.rs:65:31
|
65 | html! { <Child with props value=1 ref=() ref=() /> };
65 | html! { <Child with props value=1 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`)
--> $DIR/component-fail.rs:66:20
|
66 | html! { <Child value=1 with props ref=() ref=() /> };
66 | html! { <Child value=1 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`)
--> $DIR/component-fail.rs:67:20
|
67 | html! { <Child value=1 ref=() with props ref=() /> };
67 | html! { <Child value=1 ref={()} with props ref={()} /> };
| ^^^^^
error: `ref` can only be set once
--> $DIR/component-fail.rs:68:27
--> $DIR/component-fail.rs:68:29
|
68 | html! { <Child ref=() ref=() value=1 with props /> };
| ^^^
68 | html! { <Child ref={()} ref={()} value=1 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`)
--> $DIR/component-fail.rs:70:20
@ -99,64 +99,70 @@ help: you can escape reserved keywords to use them as identifiers
72 | html! { <Child r#type=0 /> };
| ^^^^^^
error: expected a valid Rust identifier
--> $DIR/component-fail.rs:73:20
error: the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.
--> $DIR/component-fail.rs:73:24
|
73 | html! { <Child invalid-prop-name=0 /> };
73 | html! { <Child ref=() /> };
| ^^
error: expected a valid Rust identifier
--> $DIR/component-fail.rs:74:20
|
74 | html! { <Child invalid-prop-name=0 /> };
| ^^^^^^^^^^^^^^^^^
error: expected an expression following this equals sign
--> $DIR/component-fail.rs:75:26
--> $DIR/component-fail.rs:76:26
|
75 | html! { <Child string= /> };
76 | html! { <Child string= /> };
| ^
error: `int` can only be specified once but is given here again
--> $DIR/component-fail.rs:76:26
--> $DIR/component-fail.rs:77:26
|
76 | html! { <Child int=1 int=2 int=3 /> };
77 | html! { <Child int=1 int=2 int=3 /> };
| ^^^
error: `int` can only be specified once but is given here again
--> $DIR/component-fail.rs:76:32
--> $DIR/component-fail.rs:77:32
|
76 | html! { <Child int=1 int=2 int=3 /> };
77 | html! { <Child int=1 int=2 int=3 /> };
| ^^^
error: `ref` can only be specified once
--> $DIR/component-fail.rs:81:26
--> $DIR/component-fail.rs:82:26
|
81 | html! { <Child int=1 ref=() ref=() /> };
82 | html! { <Child int=1 ref={()} ref={()} /> };
| ^^^
error: this closing tag has no corresponding opening tag
--> $DIR/component-fail.rs:84:13
--> $DIR/component-fail.rs:85:13
|
84 | html! { </Child> };
85 | html! { </Child> };
| ^^^^^^^^
error: this opening tag has no corresponding closing tag
--> $DIR/component-fail.rs:85:13
--> $DIR/component-fail.rs:86:13
|
85 | html! { <Child><Child></Child> };
86 | html! { <Child><Child></Child> };
| ^^^^^^^
error: only one root html element is allowed (hint: you can wrap multiple html elements in a fragment `<></>`)
--> $DIR/component-fail.rs:86:28
--> $DIR/component-fail.rs:87:28
|
86 | html! { <Child></Child><Child></Child> };
87 | html! { <Child></Child><Child></Child> };
| ^^^^^^^^^^^^^^^
error: cannot specify the `children` prop when the component already has children
--> $DIR/component-fail.rs:105:25
--> $DIR/component-fail.rs:106:25
|
105 | <ChildContainer children=children>
106 | <ChildContainer children={children}>
| ^^^^^^^^
error: only one root html element is allowed (hint: you can wrap multiple html elements in a fragment `<></>`)
--> $DIR/component-fail.rs:112:9
--> $DIR/component-fail.rs:113:9
|
112 | <span>{ 2 }</span>
113 | <span>{ 2 }</span>
| ^^^^^^^^^^^^^^^^^^
error[E0425]: cannot find value `blah` in this scope
@ -183,32 +189,32 @@ error[E0599]: no method named `r#type` found for struct `ChildPropertiesBuilder<
| ^^^^ method not found in `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>`
error[E0609]: no field `unknown` on type `ChildProperties`
--> $DIR/component-fail.rs:74:20
--> $DIR/component-fail.rs:75:20
|
74 | html! { <Child unknown="unknown" /> };
75 | html! { <Child unknown="unknown" /> };
| ^^^^^^^ unknown field
|
= 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
--> $DIR/component-fail.rs:74:20
--> $DIR/component-fail.rs:75:20
|
4 | #[derive(Clone, Properties, PartialEq)]
| ---------- method `unknown` not found for this
...
74 | html! { <Child unknown="unknown" /> };
75 | html! { <Child unknown="unknown" /> };
| ^^^^^^^ method not found in `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>`
error[E0277]: the trait bound `(): IntoPropValue<String>` is not satisfied
--> $DIR/component-fail.rs:77:33
--> $DIR/component-fail.rs:78:33
|
77 | html! { <Child int=1 string={} /> };
78 | html! { <Child int=1 string={} /> };
| ^^ the trait `IntoPropValue<String>` is not implemented for `()`
error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfied
--> $DIR/component-fail.rs:78:33
--> $DIR/component-fail.rs:79:33
|
78 | html! { <Child int=1 string=3 /> };
79 | html! { <Child int=1 string=3 /> };
| ^ the trait `IntoPropValue<String>` is not implemented for `{integer}`
|
= help: the following implementations were found:
@ -219,10 +225,10 @@ error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfie
and 11 others
error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfied
--> $DIR/component-fail.rs:79:26
--> $DIR/component-fail.rs:80:34
|
79 | html! { <Child int=1 string={3} /> };
| ^^^^^^ the trait `IntoPropValue<String>` is not implemented for `{integer}`
80 | html! { <Child int=1 string={3} /> };
| ^ the trait `IntoPropValue<String>` is not implemented for `{integer}`
|
= help: the following implementations were found:
<&'static str as IntoPropValue<Cow<'static, str>>>
@ -232,91 +238,91 @@ error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfie
and 11 others
error[E0308]: mismatched types
--> $DIR/component-fail.rs:80:30
--> $DIR/component-fail.rs:81:31
|
80 | html! { <Child int=1 ref=() /> };
| ^^ expected struct `NodeRef`, found `()`
81 | html! { <Child int=1 ref={()} /> };
| ^^ expected struct `NodeRef`, found `()`
error[E0277]: the trait bound `u32: IntoPropValue<i32>` is not satisfied
--> $DIR/component-fail.rs:82:24
--> $DIR/component-fail.rs:83:24
|
82 | html! { <Child int=0u32 /> };
83 | html! { <Child int=0u32 /> };
| ^^^^ 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
--> $DIR/component-fail.rs:83:20
--> $DIR/component-fail.rs:84:20
|
4 | #[derive(Clone, Properties, PartialEq)]
| ---------- method `string` not found for this
...
83 | html! { <Child string="abc" /> };
84 | html! { <Child string="abc" /> };
| ^^^^^^ method not found in `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>`
error[E0609]: no field `children` on type `ChildProperties`
--> $DIR/component-fail.rs:87:14
--> $DIR/component-fail.rs:88:14
|
87 | html! { <Child>{ "Not allowed" }</Child> };
88 | html! { <Child>{ "Not allowed" }</Child> };
| ^^^^^ unknown field
|
= 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
--> $DIR/component-fail.rs:87:14
--> $DIR/component-fail.rs:88:14
|
4 | #[derive(Clone, Properties, PartialEq)]
| ---------- method `children` not found for this
...
87 | html! { <Child>{ "Not allowed" }</Child> };
88 | html! { <Child>{ "Not allowed" }</Child> };
| ^^^^^ method not found in `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>`
error[E0609]: no field `children` on type `ChildProperties`
--> $DIR/component-fail.rs:91:10
--> $DIR/component-fail.rs:92:10
|
91 | <Child with ChildProperties { string: "hello".to_owned(), int: 5 }>
92 | <Child with ChildProperties { string: "hello".to_owned(), int: 5 }>
| ^^^^^ unknown field
|
= 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
--> $DIR/component-fail.rs:96:14
|
30 | #[derive(Clone, Properties)]
| ---------- method `build` not found for this
...
96 | html! { <ChildContainer /> };
| ^^^^^^^^^^^^^^ 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
--> $DIR/component-fail.rs:97:14
|
30 | #[derive(Clone, Properties)]
| ---------- method `build` not found for this
...
97 | html! { <ChildContainer></ChildContainer> };
97 | html! { <ChildContainer /> };
| ^^^^^^^^^^^^^^ 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
--> $DIR/component-fail.rs:98:14
|
30 | #[derive(Clone, Properties)]
| ---------- method `build` not found for this
...
98 | html! { <ChildContainer></ChildContainer> };
| ^^^^^^^^^^^^^^ 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
--> $DIR/component-fail.rs:98:31
--> $DIR/component-fail.rs:99:31
|
98 | html! { <ChildContainer>{ "Not allowed" }</ChildContainer> };
99 | html! { <ChildContainer>{ "Not allowed" }</ChildContainer> };
| ^^^^^^^^^^^^^ 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 by `into`
error[E0277]: the trait bound `VChild<Child>: From<VNode>` is not satisfied
--> $DIR/component-fail.rs:99:29
|
99 | html! { <ChildContainer><></></ChildContainer> };
| ^ 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 by `into`
--> $DIR/component-fail.rs:100:29
|
100 | html! { <ChildContainer><></></ChildContainer> };
| ^ 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 by `into`
error[E0277]: the trait bound `VChild<Child>: From<VNode>` is not satisfied
--> $DIR/component-fail.rs:100:30
--> $DIR/component-fail.rs:101:30
|
100 | html! { <ChildContainer><other /></ChildContainer> };
101 | html! { <ChildContainer><other /></ChildContainer> };
| ^^^^^ 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`

View File

@ -152,8 +152,8 @@ fn compile_pass() {
html! {
<>
<Child with props />
<Child ref=node_ref.clone() with yew::props!(Child::Properties { int: 5 }) />
<Child with <Child as Component>::Properties::default() ref=node_ref />
<Child ref={node_ref.clone()} with yew::props!(Child::Properties { int: 5 }) />
<Child with <Child as Component>::Properties::default() ref={node_ref} />
</>
};
@ -165,29 +165,31 @@ fn compile_pass() {
<Child int=1 vec={vec![1]} />
<Child string={String::from("child")} int=1 />
<Child opt_str=String::from("child") int=1 />
<Child opt_str=Some(String::from("child")) int=1 />
<Child opt_str="child" int=1 />
<Child opt_str={String::from("child")} int=1 />
<Child opt_str={Some("child")} int=1 />
<Child opt_str={Some(String::from("child"))} int=1 />
</>
};
let name_expr = "child";
html! {
<Child int=1 string=name_expr />
<Child int=1 string={name_expr} />
};
html! {
<>
<Child int=1 />
<Child int=1 optional_callback=Some(Callback::from(|_| ())) />
<Child int=1 optional_callback=Callback::from(|_| ()) />
<Child int=1 optional_callback=None::<Callback<_>> />
<Child int=1 optional_callback={Some(Callback::from(|_| ()))} />
<Child int=1 optional_callback={Callback::from(|_| ())} />
<Child int=1 optional_callback={None::<Callback<_>>} />
</>
};
let node_ref = NodeRef::default();
html! {
<>
<Child int=1 ref=node_ref />
<Child int=1 ref={node_ref} />
</>
};
@ -209,9 +211,9 @@ fn compile_pass() {
<scoped::Container int=2/>
</scoped::Container>
<Container int=1 children=ChildrenRenderer::new(
<Container int=1 children={ChildrenRenderer::new(
vec![html!{ "String" }]
) />
)} />
</>
};
@ -235,7 +237,7 @@ fn compile_pass() {
}
{
(0..2)
.map(|i| { html_nested! { <Child int=i /> } })
.map(|i| { html_nested! { <Child int={i} /> } })
.collect::<Vec<_>>()
}
</ChildContainer>

View File

@ -30,31 +30,37 @@ fn compile_fail() {
html! { <input disabled=true disabled=false /> };
html! { <option selected=true selected=false /> };
html! { <div class="first" class="second" /> };
html! { <input ref=() ref=() /> };
html! { <input ref={()} ref={()} /> };
// boolean attribute type mismatch
html! { <input checked=1 /> };
html! { <input checked=Some(false) /> };
html! { <input checked={Some(false)} /> };
html! { <input disabled=1 /> };
html! { <input disabled=Some(true) /> };
html! { <input disabled={Some(true)} /> };
html! { <option selected=1 /> };
// normal attribute type mismatch
html! { <input type=() /> };
html! { <input value=() /> };
html! { <a href=() /> };
html! { <input string=NotToString /> };
html! { <a media=Some(NotToString) /> };
html! { <a href=Some(5) /> };
html! { <input type={()} /> };
html! { <input value={()} /> };
html! { <a href={()} /> };
html! { <input string={NotToString} /> };
html! { <a media={Some(NotToString)} /> };
html! { <a href={Some(5)} /> };
// listener type mismatch
html! { <input onclick=1 /> };
html! { <input onclick=Callback::from(|a: String| ()) /> };
html! { <input onfocus=Some(5) /> };
html! { <input onclick={Callback::from(|a: String| ()) /> };
html! { <input onfocus={Some(5)} /> };
// NodeRef type mismatch
html! { <input ref=() /> };
html! { <input ref=Some(NodeRef::default()) /> };
html! { <input ref={()} /> };
html! { <input ref={Some(NodeRef::default())} /> };
html! { <input onclick={Callback::from(|a: String| ())} /> };
html! { <input string={NotToString} /> };
html! { <input ref={()} /> };
html! { <input ref={()} ref={()} /> };
// void element with children
html! { <input type="text"></input> };
@ -71,7 +77,19 @@ fn compile_fail() {
html! { <@{55}></@> };
// check for deprecation warning
html! { <div class={("deprecated", "warning")} /> };
// Missing curly braces
html! { <div class=("deprecated", "warning") /> };
html! { <input ref=() /> };
html! { <input ref=() ref=() /> };
html! { <input onfocus=Some(5) /> };
html! { <input string=NotToString /> };
html! { <a media=Some(NotToString) /> };
html! { <a href=Some(5) /> };
html! { <input type=() /> };
html! { <input value=() /> };
html! { <input string=NotToString /> };
}
fn main() {}

View File

@ -1,3 +1,18 @@
error: this file contains an unclosed delimiter
--> $DIR/element-fail.rs:95:14
|
5 | fn compile_fail() {
| - unclosed delimiter
...
52 | html! { <input onclick={Callback::from(|a: String| ()) /> };
| - this delimiter might not be properly closed...
...
93 | }
| - ...as it matches this but it has different indentation
94 |
95 | fn main() {}
| ^
error: this opening tag has no corresponding closing tag
--> $DIR/element-fail.rs:7:13
|
@ -103,46 +118,22 @@ error: `class` can only be specified once but is given here again
error: `ref` can only be specified once
--> $DIR/element-fail.rs:33:20
|
33 | html! { <input ref=() ref=() /> };
33 | html! { <input ref={()} ref={()} /> };
| ^^^
error: the tag `<input>` is a void element and cannot have children (hint: rewrite this as `<input/>`)
--> $DIR/element-fail.rs:60:13
error: unexpected end of input, expected token tree
--> $DIR/element-fail.rs:52:5
|
60 | html! { <input type="text"></input> };
| ^^^^^^^^^^^^^^^^^^^
error: the tag `<iNpUt>` is a void element and cannot have children (hint: rewrite this as `<iNpUt/>`)
--> $DIR/element-fail.rs:62:13
52 | / html! { <input onclick={Callback::from(|a: String| ()) /> };
53 | | html! { <input onfocus={Some(5)} /> };
54 | |
55 | | // NodeRef type mismatch
... |
92 | | html! { <input string=NotToString /> };
93 | | }
| |_^
|
62 | html! { <iNpUt type="text"></iNpUt> };
| ^^^^^^^^^^^^^^^^^^^
error: this dynamic tag is missing an expression block defining its value
--> $DIR/element-fail.rs:65:14
|
65 | html! { <@></@> };
| ^
error: this dynamic tag is missing an expression block defining its value
--> $DIR/element-fail.rs:66:14
|
66 | html! { <@/> };
| ^
error: dynamic closing tags must not have a body (hint: replace it with just `</@>`)
--> $DIR/element-fail.rs:69:27
|
69 | html! { <@{"test"}></@{"test"}> };
| ^^^^^^^^
warning: use of deprecated function `compile_fail::deprecated_use_of_class`: the use of `(...)` with the attribute `class` is deprecated and will be removed in version 0.19. Use the `classes!` macro instead.
--> $DIR/element-fail.rs:74:24
|
74 | html! { <div class=("deprecated", "warning") /> };
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(deprecated)]` on by default
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0308]: mismatched types
--> $DIR/element-fail.rs:36:28
@ -151,10 +142,10 @@ error[E0308]: mismatched types
| ^ expected `bool`, found integer
error[E0308]: mismatched types
--> $DIR/element-fail.rs:37:28
--> $DIR/element-fail.rs:37:29
|
37 | html! { <input checked=Some(false) /> };
| ^^^^^^^^^^^ expected `bool`, found enum `Option`
37 | html! { <input checked={Some(false)} /> };
| ^^^^^^^^^^^ expected `bool`, found enum `Option`
|
= note: expected type `bool`
found enum `Option<bool>`
@ -166,10 +157,10 @@ error[E0308]: mismatched types
| ^ expected `bool`, found integer
error[E0308]: mismatched types
--> $DIR/element-fail.rs:39:29
--> $DIR/element-fail.rs:39:30
|
39 | html! { <input disabled=Some(true) /> };
| ^^^^^^^^^^ expected `bool`, found enum `Option`
39 | html! { <input disabled={Some(true)} /> };
| ^^^^^^^^^^ expected `bool`, found enum `Option`
|
= note: expected type `bool`
found enum `Option<bool>`
@ -181,10 +172,10 @@ error[E0308]: mismatched types
| ^ expected `bool`, found integer
error[E0277]: the trait bound `(): IntoPropValue<Option<Cow<'static, str>>>` is not satisfied
--> $DIR/element-fail.rs:43:25
--> $DIR/element-fail.rs:43:26
|
43 | html! { <input type=() /> };
| ^^ the trait `IntoPropValue<Option<Cow<'static, str>>>` is not implemented for `()`
43 | html! { <input type={()} /> };
| ^^ the trait `IntoPropValue<Option<Cow<'static, str>>>` is not implemented for `()`
|
::: $WORKSPACE/packages/yew/src/virtual_dom/mod.rs
|
@ -192,18 +183,18 @@ error[E0277]: the trait bound `(): IntoPropValue<Option<Cow<'static, str>>>` is
| -------------------------------- required by this bound in `PositionalAttr::new`
error[E0277]: the trait bound `(): IntoPropValue<Option<Cow<'static, str>>>` is not satisfied
--> $DIR/element-fail.rs:44:26
--> $DIR/element-fail.rs:44:27
|
44 | html! { <input value=() /> };
| ^^ the trait `IntoPropValue<Option<Cow<'static, str>>>` is not implemented for `()`
44 | html! { <input value={()} /> };
| ^^ the trait `IntoPropValue<Option<Cow<'static, str>>>` is not implemented for `()`
|
= note: required by `into_prop_value`
error[E0277]: the trait bound `(): IntoPropValue<Option<Cow<'static, str>>>` is not satisfied
--> $DIR/element-fail.rs:45:21
--> $DIR/element-fail.rs:45:22
|
45 | html! { <a href=() /> };
| ^^ the trait `IntoPropValue<Option<Cow<'static, str>>>` is not implemented for `()`
45 | html! { <a href={()} /> };
| ^^ the trait `IntoPropValue<Option<Cow<'static, str>>>` is not implemented for `()`
|
::: $WORKSPACE/packages/yew/src/virtual_dom/mod.rs
|
@ -211,10 +202,10 @@ error[E0277]: the trait bound `(): IntoPropValue<Option<Cow<'static, str>>>` is
| -------------------------------- required by this bound in `PositionalAttr::new`
error[E0277]: the trait bound `NotToString: IntoPropValue<Option<Cow<'static, str>>>` is not satisfied
--> $DIR/element-fail.rs:46:27
--> $DIR/element-fail.rs:46:28
|
46 | html! { <input string=NotToString /> };
| ^^^^^^^^^^^ the trait `IntoPropValue<Option<Cow<'static, str>>>` is not implemented for `NotToString`
46 | html! { <input string={NotToString} /> };
| ^^^^^^^^^^^ the trait `IntoPropValue<Option<Cow<'static, str>>>` is not implemented for `NotToString`
|
::: $WORKSPACE/packages/yew/src/virtual_dom/mod.rs
|
@ -222,10 +213,10 @@ error[E0277]: the trait bound `NotToString: IntoPropValue<Option<Cow<'static, st
| -------------------------------- required by this bound in `PositionalAttr::new`
error[E0277]: the trait bound `Option<NotToString>: IntoPropValue<Option<Cow<'static, str>>>` is not satisfied
--> $DIR/element-fail.rs:47:22
--> $DIR/element-fail.rs:47:23
|
47 | html! { <a media=Some(NotToString) /> };
| ^^^^^^^^^^^^^^^^^ the trait `IntoPropValue<Option<Cow<'static, str>>>` is not implemented for `Option<NotToString>`
47 | html! { <a media={Some(NotToString)} /> };
| ^^^^^^^^^^^^^^^^^ the trait `IntoPropValue<Option<Cow<'static, str>>>` is not implemented for `Option<NotToString>`
|
::: $WORKSPACE/packages/yew/src/virtual_dom/mod.rs
|
@ -238,10 +229,10 @@ error[E0277]: the trait bound `Option<NotToString>: IntoPropValue<Option<Cow<'st
<Option<String> as IntoPropValue<Option<Cow<'static, str>>>>
error[E0277]: the trait bound `Option<{integer}>: IntoPropValue<Option<Cow<'static, str>>>` is not satisfied
--> $DIR/element-fail.rs:48:21
--> $DIR/element-fail.rs:48:22
|
48 | html! { <a href=Some(5) /> };
| ^^^^^^^ the trait `IntoPropValue<Option<Cow<'static, str>>>` is not implemented for `Option<{integer}>`
48 | html! { <a href={Some(5)} /> };
| ^^^^^^^ the trait `IntoPropValue<Option<Cow<'static, str>>>` is not implemented for `Option<{integer}>`
|
::: $WORKSPACE/packages/yew/src/virtual_dom/mod.rs
|
@ -276,77 +267,3 @@ error[E0277]: the trait bound `{integer}: IntoPropValue<Option<yew::Callback<Mou
<&'static str as IntoPropValue<Option<String>>>
<&'static str as IntoPropValue<String>>
and 11 others
error[E0277]: the trait bound `yew::Callback<String>: IntoPropValue<Option<yew::Callback<MouseEvent>>>` is not satisfied
--> $DIR/element-fail.rs:52:28
|
52 | html! { <input onclick=Callback::from(|a: String| ()) /> };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IntoPropValue<Option<yew::Callback<MouseEvent>>>` is not implemented for `yew::Callback<String>`
|
::: $WORKSPACE/packages/yew/src/html/listener/events.rs
|
| / impl_action! {
3 | | onabort(name: "abort", event: Event) -> web_sys::Event => |_, event| { event }
4 | | onauxclick(name: "auxclick", event: MouseEvent) -> web_sys::MouseEvent => |_, event| { event }
5 | | onblur(name: "blur", event: FocusEvent) -> web_sys::FocusEvent => |_, event| { event }
... |
101 | | ontransitionstart(name: "transitionstart", event: TransitionEvent) -> web_sys::TransitionEvent => |_, event| { event }
102 | | }
| |_- required by this bound in `yew::html::onclick::Wrapper::__macro_new`
error[E0277]: the trait bound `Option<{integer}>: IntoPropValue<Option<yew::Callback<FocusEvent>>>` is not satisfied
--> $DIR/element-fail.rs:53:28
|
53 | html! { <input onfocus=Some(5) /> };
| ^^^^^^^ the trait `IntoPropValue<Option<yew::Callback<FocusEvent>>>` is not implemented for `Option<{integer}>`
|
::: $WORKSPACE/packages/yew/src/html/listener/events.rs
|
| / impl_action! {
3 | | onabort(name: "abort", event: Event) -> web_sys::Event => |_, event| { event }
4 | | onauxclick(name: "auxclick", event: MouseEvent) -> web_sys::MouseEvent => |_, event| { event }
5 | | onblur(name: "blur", event: FocusEvent) -> web_sys::FocusEvent => |_, event| { event }
... |
101 | | ontransitionstart(name: "transitionstart", event: TransitionEvent) -> web_sys::TransitionEvent => |_, event| { event }
102 | | }
| |_- required by this bound in `yew::html::onfocus::Wrapper::__macro_new`
|
= help: the following implementations were found:
<Option<&'static str> as IntoPropValue<Option<Cow<'static, str>>>>
<Option<&'static str> as IntoPropValue<Option<String>>>
<Option<String> as IntoPropValue<Option<Cow<'static, str>>>>
error[E0277]: the trait bound `(): IntoPropValue<yew::NodeRef>` is not satisfied
--> $DIR/element-fail.rs:56:24
|
56 | html! { <input ref=() /> };
| ^^ the trait `IntoPropValue<yew::NodeRef>` is not implemented for `()`
|
= note: required by `into_prop_value`
error[E0277]: the trait bound `Option<yew::NodeRef>: IntoPropValue<yew::NodeRef>` is not satisfied
--> $DIR/element-fail.rs:57:24
|
57 | html! { <input ref=Some(NodeRef::default()) /> };
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IntoPropValue<yew::NodeRef>` is not implemented for `Option<yew::NodeRef>`
|
= help: the following implementations were found:
<Option<&'static str> as IntoPropValue<Option<Cow<'static, str>>>>
<Option<&'static str> as IntoPropValue<Option<String>>>
<Option<String> as IntoPropValue<Option<Cow<'static, str>>>>
= note: required by `into_prop_value`
error[E0277]: the trait bound `Cow<'static, str>: From<{integer}>` is not satisfied
--> $DIR/element-fail.rs:71:15
|
71 | html! { <@{55}></@> };
| ^^^^ the trait `From<{integer}>` is not implemented for `Cow<'static, str>`
|
= help: the following implementations were found:
<Cow<'a, CStr> as From<&'a CStr>>
<Cow<'a, CStr> as From<&'a CString>>
<Cow<'a, CStr> as From<CString>>
<Cow<'a, OsStr> as From<&'a OsStr>>
and 11 others
= note: required because of the requirements on the impl of `Into<Cow<'static, str>>` for `{integer}`
= note: required by `into`

View File

@ -13,7 +13,7 @@ fn compile_pass() {
html! {
<div>
<div data-key="abc"></div>
<div ref=parent_ref class="parent">
<div ref={parent_ref} class="parent">
<span class="child" value="anything"></span>
<label for="first-name">{"First Name"}</label>
<input type="text" id="first-name" value="placeholder" />
@ -40,9 +40,9 @@ fn compile_pass() {
</filter>
</defs>
</svg>
<img class=classes!("avatar", "hidden") src="http://pic.com" />
<img class={classes!("avatar", "hidden")} src="http://pic.com" />
<img class="avatar hidden" />
<button onclick=&onclick onclick=onclick />
<button onclick={&onclick} onclick={onclick} />
<a href="http://google.com" />
<custom-tag-a>
<custom-tag-b />
@ -61,10 +61,10 @@ fn compile_pass() {
}
}/>
<a href=Some(Cow::Borrowed("http://google.com")) media=cow_none.clone() />
<track kind=Some(Cow::Borrowed("subtitles")) src=cow_none.clone() />
<track kind=Some(Cow::Borrowed("5")) mixed="works" />
<input value=Some(Cow::Borrowed("value")) onblur=Some(Callback::from(|_| ())) />
<a href={Some(Cow::Borrowed("http://google.com"))} media={cow_none.clone()} />
<track kind={Some(Cow::Borrowed("subtitles"))} src={cow_none.clone()} />
<track kind={Some(Cow::Borrowed("5"))} mixed="works" />
<input value={Some(Cow::Borrowed("value"))} onblur={Some(Callback::from(|_| ()))} />
</div>
};
@ -75,8 +75,8 @@ fn compile_pass() {
html! { <div>{children}</div> };
// handle misleading angle brackets
html! { <div data-val=<String as Default>::default()></div> };
html! { <div><a data-val=<String as Default>::default() /></div> };
html! { <div data-val={<String as Default>::default()}></div> };
html! { <div><a data-val={<String as Default>::default()} /></div> };
}
fn main() {}

View File

@ -46,11 +46,11 @@ error: expected an expression following this equals sign
18 | html! { <key=></> };
| ^^
error: this closing tag has no corresponding opening tag
--> $DIR/list-fail.rs:20:36
error: the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.
--> $DIR/list-fail.rs:20:18
|
20 | html! { <key="key".to_string()></key> };
| ^^^^^^
| ^^^^^^^^^^^^^^^^^
error: only a single `key` prop is allowed on a fragment
--> $DIR/list-fail.rs:23:30

View File

@ -10,7 +10,7 @@ fn main() {
</>
};
::yew::html! {
<key=::std::string::ToString::to_string("key")>
<key={::std::string::ToString::to_string("key")}>
</>
};

View File

@ -27,7 +27,7 @@ fn switch(routes: &Route) -> Html {
Route::Secure => html! {
<div>
<h1>{ "Secure" }</h1>
<button onclick=onclick_callback>{ "Go Home" }</button>
<button onclick={onclick_callback}>{ "Go Home" }</button>
</div>
},
Route::NotFound => html! { <h1>{ "404" }</h1> },
@ -36,7 +36,7 @@ fn switch(routes: &Route) -> Html {
// Component's `view` method
html! {
<Router<Route> render=Router::render(switch) />
<Router<Route> render={Router::render(switch)} />
}
```

View File

@ -46,12 +46,12 @@ impl<R: Routable + Clone + PartialEq + 'static> Component for Link<R> {
fn view(&self) -> Html {
html! {
<a class=self.props.classes.clone()
href=self.props.route.to_path()
onclick=self.link.callback(|e: MouseEvent| {
<a class={self.props.classes.clone()}
href={self.props.route.to_path()}
onclick={self.link.callback(|e: MouseEvent| {
e.prevent_default();
Msg::OnClick
})
})}
>
{ self.props.children.clone() }
</a>

View File

@ -22,7 +22,7 @@
//! # #[function_component(Main)]
//! # fn app() -> Html {
//! html! {
//! <Router<Route> render=Router::render(switch) />
//! <Router<Route> render={Router::render(switch)} />
//! }
//! # }
//!
@ -33,7 +33,7 @@
//! Route::Secure => html! {
//! <div>
//! <h1>{ "Secure" }</h1>
//! <button onclick=onclick_callback>{ "Go Home" }</button>
//! <button onclick={onclick_callback}>{ "Go Home" }</button>
//! </div>
//! },
//! Route::NotFound => html! { <h1>{ "404" }</h1> },

View File

@ -58,16 +58,16 @@ fn component() -> Html {
Routes::Home => html! {
<>
<div id="result">{"Home"}</div>
<a onclick=onclick>{"click me"}</a>
<a onclick={onclick}>{"click me"}</a>
</>
},
Routes::No { id } => html! { <No id=*id /> },
Routes::No { id } => html! { <No id={*id} /> },
Routes::NotFound => html! { <div id="result">{"404"}</div> },
}
});
html! {
<Router<Routes> render=switch>
<Router<Routes> render={switch}>
</Router<Routes>>
}
}

View File

@ -22,7 +22,7 @@ use crate::functional::{get_current_scope, use_hook};
/// let theme = use_context::<Rc<ThemeContext>>().expect("no ctx found");
///
/// html! {
/// <button style=format!("background: {}; color: {}", theme.background, theme.foreground)>
/// <button style={format!("background: {}; color: {}", theme.background, theme.foreground)}>
/// { "Click me" }
/// </button>
/// }

View File

@ -31,7 +31,7 @@ struct UseEffect<Destructor> {
/// };
///
/// html! {
/// <button onclick=onclick>{ format!("Increment to {}", *counter) }</button>
/// <button onclick={onclick}>{ format!("Increment to {}", *counter) }</button>
/// }
/// }
/// ```

View File

@ -56,8 +56,8 @@ struct UseReducer<State> {
/// <>
/// <div id="result">{ counter.counter }</div>
///
/// <button onclick=double_onclick>{ "Double" }</button>
/// <button onclick=square_onclick>{ "Square" }</button>
/// <button onclick={double_onclick}>{ "Double" }</button>
/// <button onclick={square_onclick}>{ "Square" }</button>
/// </>
/// }
/// }
@ -103,7 +103,7 @@ where
/// <>
/// <div id="result">{counter.counter}</div>
///
/// <button onclick=Callback::from(move |_| counter.dispatch(10))>{"Increment by 10"}</button>
/// <button onclick={Callback::from(move |_| counter.dispatch(10))}>{"Increment by 10"}</button>
/// </>
/// }
/// }

View File

@ -41,8 +41,8 @@ use std::{cell::RefCell, rc::Rc};
///
/// html! {
/// <div>
/// <input onchange=onchange value=(*message).clone() />
/// <button onclick=onclick>{ "Send" }</button>
/// <input onchange={onchange} value={(*message).clone()} />
/// <button onclick={onclick}>{ "Send" }</button>
/// </div>
/// }
/// }

View File

@ -25,7 +25,7 @@ struct UseState<T2> {
///
/// html! {
/// <div>
/// <button onclick=onclick>{ "Increment value" }</button>
/// <button onclick={onclick}>{ "Increment value" }</button>
/// <p>
/// <b>{ "Current value: " }</b>
/// { *counter }

View File

@ -291,7 +291,7 @@ mod tests {
self.link.send_message(msg);
}
self.props.lifecycle.borrow_mut().push("view".into());
html! { <Child lifecycle=self.props.lifecycle.clone() /> }
html! { <Child lifecycle={self.props.lifecycle.clone()} /> }
}
}

View File

@ -59,7 +59,7 @@ pub type Html = VNode;
///
/// fn view(&self) -> Html {
/// html! {
/// <input ref=self.node_ref.clone() type="text" />
/// <input ref={self.node_ref.clone()} type="text" />
/// }
/// }
/// }

View File

@ -56,7 +56,7 @@
//! fn view(&self) -> Html {
//! html! {
//! <div>
//! <button onclick=self.link.callback(|_| Msg::AddOne)>{ "+1" }</button>
//! <button onclick={self.link.callback(|_| Msg::AddOne)}>{ "+1" }</button>
//! <p>{ self.value }</p>
//! </div>
//! }
@ -98,7 +98,7 @@ use std::{cell::Cell, panic::PanicInfo};
/// let vec_of_classes = vec!["one-bean", "two-beans", "three-beans", "a-very-small-casserole"];
///
/// html! {
/// <div class=classes!("my-container-class", conditional_class, vec_of_classes)>
/// <div class={classes!("my-container-class", conditional_class, vec_of_classes)}>
/// // ...
/// </div>
/// };

View File

@ -82,8 +82,8 @@ mod test {
fn all_key_conversions() {
html! {
<key="string literal">
<img key="String".to_owned() />
<p key=Rc::<str>::from("rc")></p>
<img key={"String".to_owned()} />
<p key={Rc::<str>::from("rc")}></p>
<key='a'>
<p key=11_usize></p>
<p key=12_u8></p>

View File

@ -320,11 +320,11 @@ mod tests {
};
let props_2 = props.clone();
check_key(html! { <Comp key=test_key.clone() /> });
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 with props key=test_key.clone() /> });
check_key(html! { <Comp key=test_key.clone() with props_2 /> });
check_key(html! { <Comp key={test_key.clone()} /> });
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 with props key={test_key.clone()} /> });
check_key(html! { <Comp key={test_key.clone()} with props_2 /> });
}
#[test]
@ -341,11 +341,11 @@ mod tests {
};
let props_2 = props.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 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() with props_2 /> });
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 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()} with props_2 /> });
}
#[test]
@ -444,7 +444,7 @@ mod tests {
</ul>";
let prop_method = html! {
<List children=children_renderer.clone()/>
<List children={children_renderer.clone()} />
};
assert_eq!(get_html(prop_method, &scope, &parent), expected_html);
@ -481,7 +481,7 @@ mod tests {
document().body().unwrap().append_child(&parent).unwrap();
let node_ref = NodeRef::default();
let mut elem: VNode = html! { <Comp ref=node_ref.clone()></Comp> };
let mut elem: VNode = html! { <Comp ref={node_ref.clone()}></Comp> };
elem.apply(&scope, &parent, NodeRef::default(), None);
let parent_node = parent.deref();
assert_eq!(node_ref.get(), parent_node.first_child());

View File

@ -790,7 +790,7 @@ mod tests {
};
let d = html! {
<div class=format!("fail{}", "")></div>
<div class={format!("fail{}", "")}></div>
};
assert_eq!(a, b);
@ -976,7 +976,7 @@ mod tests {
let expected = "not_changed_value";
// Initial state
let mut elem = html! { <input value=expected /> };
let mut elem = html! { <input value={expected} /> };
elem.apply(&scope, &parent, NodeRef::default(), None);
let vtag = if let VNode::VTag(vtag) = elem {
vtag
@ -990,7 +990,7 @@ mod tests {
input.unwrap().set_value("User input");
let ancestor = vtag;
let mut elem = html! { <input value=expected /> };
let mut elem = html! { <input value={expected} /> };
let vtag = assert_vtag_mut(&mut elem);
// Sync happens here
@ -1115,7 +1115,7 @@ mod tests {
document().body().unwrap().append_child(&parent).unwrap();
let node_ref = NodeRef::default();
let mut elem: VNode = html! { <div ref=node_ref.clone()></div> };
let mut elem: VNode = html! { <div ref={node_ref.clone()}></div> };
assert_vtag_mut(&mut elem);
elem.apply(&scope, &parent, NodeRef::default(), None);
let parent_node = parent.deref();
@ -1123,32 +1123,6 @@ mod tests {
elem.detach(&parent);
assert!(node_ref.get().is_none());
}
/// Returns the class attribute as str reference, or "" if the attribute is not set.
fn get_class_str(vtag: &VTag) -> &str {
vtag.attributes
.iter()
.find(|(k, _)| k == &"class")
.map(|(_, v)| AsRef::as_ref(v))
.unwrap_or("")
}
#[test]
fn old_class_syntax_is_still_supported() {
let a_classes = "class-1 class-2".to_string();
#[allow(deprecated)]
let a = html! {
<div class=("class-1", a_classes)></div>
};
if let VNode::VTag(vtag) = a {
assert!(get_class_str(&vtag).contains("class-1"));
assert!(get_class_str(&vtag).contains("class-2"));
assert!(!get_class_str(&vtag).contains("class-3"));
} else {
panic!("vtag expected");
}
}
}
#[cfg(test)]

View File

@ -42,18 +42,18 @@ fn use_context_scoping_works() {
type ExampleContextProvider = ContextProvider<ExampleContext>;
return html! {
<div>
<ExampleContextProvider context=ExampleContext("wrong1".into())>
<ExampleContextProvider context={ExampleContext("wrong1".into())}>
<div>{"ignored"}</div>
</ExampleContextProvider>
<ExampleContextProvider context=ExampleContext("wrong2".into())>
<ExampleContextProvider context=ExampleContext("correct".into())>
<ExampleContextProvider context=ExampleContext("wrong1".into())>
<ExampleContextProvider context={ExampleContext("wrong2".into())}>
<ExampleContextProvider context={ExampleContext("correct".into())}>
<ExampleContextProvider context={ExampleContext("wrong1".into())}>
<div>{"ignored"}</div>
</ExampleContextProvider>
<UseContextComponentInner />
</ExampleContextProvider>
</ExampleContextProvider>
<ExampleContextProvider context=ExampleContext("wrong3".into())>
<ExampleContextProvider context={ExampleContext("wrong3".into())}>
<div>{"ignored"}</div>
</ExampleContextProvider>
<ExpectNoContextComponent />
@ -148,9 +148,9 @@ fn use_context_works_with_multiple_types() {
return html! {
<div>
<ContextAProvider context=ContextA(0)>
<ContextBProvider context=ContextB(1)>
<ContextAProvider context=ContextA(2)>
<ContextAProvider context={ContextA(0)}>
<ContextBProvider context={ContextB(1)}>
<ContextAProvider context={ContextA(2)}>
<Test1/>
</ContextAProvider>
<Test2/>
@ -189,7 +189,7 @@ fn use_context_update_works() {
*counter.borrow_mut() += 1;
return html! {
<>
<div id=props.id.clone()>
<div id={props.id.clone()}>
{ format!("total: {}", counter.borrow()) }
</div>
{ props.children.clone() }
@ -218,7 +218,7 @@ fn use_context_update_works() {
return html! {
<>
<div>{ format!("magic: {}\n", props.magic) }</div>
<div id=props.id.clone()>
<div id={props.id.clone()}>
{ format!("current: {}, total: {}", ctx.0, counter.borrow()) }
</div>
</>
@ -264,10 +264,10 @@ fn use_context_update_works() {
});
}
return html! {
<MyContextProvider context=Rc::new((*ctx).clone())>
<MyContextProvider context={Rc::new((*ctx).clone())}>
<RenderCounter id="test-0">
<ContextOutlet id="test-1"/>
<ContextOutlet id="test-2" magic=magic/>
<ContextOutlet id="test-2" magic={magic}/>
</RenderCounter>
</MyContextProvider>
};

View File

@ -60,7 +60,7 @@ fn use_effect_destroys_on_component_drop() {
if *show {
let effect_called: Rc<dyn Fn()> = { Rc::new(move || show.set(false)) };
html! {
<UseEffectComponent destroy_called=props.destroy_called.clone() effect_called=effect_called />
<UseEffectComponent destroy_called={props.destroy_called.clone()} effect_called={effect_called} />
}
} else {
html! {

View File

@ -57,7 +57,7 @@ impl Component for MyComponent {
fn view(&self) -> Html {
let onclick = self.link.callback(|_| Msg::Click);
html! {
<button onclick=onclick>{ self.props.button_text }</button>
<button onclick={onclick}>{ self.props.button_text }</button>
}
}
}
@ -86,7 +86,7 @@ impl Component for MyComponent {
fn view(&self) -> Html {
html! {
<input ref=self.node_ref.clone() type="text" />
<input ref={self.node_ref.clone()} type="text" />
}
}

View File

@ -71,7 +71,7 @@ A simple use of a callback might look something like this:
```rust
let onclick = self.link.callback(|_| Msg::Clicked);
html! {
<button onclick=onclick>{ "Click" }</button>
<button onclick={onclick}>{ "Click" }</button>
}
```
@ -89,7 +89,7 @@ let onkeypress = self.link.batch_callback(|event| {
});
html! {
<input type="text" onkeypress=onkeypress />
<input type="text" onkeypress={onkeypress} />
}
```

View File

@ -169,8 +169,8 @@ The page component can be called either with the sidebar or without:
// Page with sidebar
html! {
<Page sidebar=html_nested! {
<Page sidebar={html_nested! {
<PageSideBar />
} />
}} />
}
```

View File

@ -19,7 +19,7 @@ self.node_ref = NodeRef::default();
// In view
html! {
<div ref=self.node_ref.clone()></div>
<div ref={self.node_ref.clone()}></div>
}
// In rendered

View File

@ -11,14 +11,14 @@ theme using props:
// root
let theme = // ...
html! {
<Navbar theme=theme />
<Navbar theme={theme} />
}
// Navbar component
html! {
<div>
<Title theme=theme>{ "App title" }<Title>
<NavButton theme=theme>{ "Somewhere" }</NavButton>
<Title theme={theme}>{ "App title" }<Title>
<NavButton theme={theme}>{ "Somewhere" }</NavButton>
</div>
}
```
@ -63,7 +63,7 @@ impl Component for ContextDemo {
fn view(&self) -> Html {
let theme = self.link.context::<Theme>();
html! {
<button style=format!("background: {}; color: {};", theme.background, theme.foreground)>
<button style={format!("background: {}; color: {};", theme.background, theme.foreground)}>
{ "Click me!" }
</button>
}

View File

@ -51,7 +51,7 @@ fn app() -> Html {
html! {
<div>
<button onclick=onclick>{ "Increment value" }</button>
<button onclick={onclick}>{ "Increment value" }</button>
<p>
<b>{ "Current value: " }</b>
{ counter }
@ -92,6 +92,6 @@ html! {
}
// or
html! {
<MyGenericComponent<Foo> data=foo />
<MyGenericComponent<Foo> data={foo} />
}
```

View File

@ -26,7 +26,7 @@ fn state() -> Html {
html! {
<div>
<button onclick=onclick>{ "Increment value" }</button>
<button onclick={onclick}>{ "Increment value" }</button>
<p>
<b>{ "Current value: " }</b>
{ *counter }
@ -70,8 +70,8 @@ fn ref_hook() -> Html {
html! {
<div>
<input onchange=onchange value=message />
<button onclick=onclick>{ "Send" }</button>
<input onchange={onchange} value={message} />
<button onclick={onclick}>{ "Send" }</button>
</div>
}
}
@ -131,8 +131,8 @@ fn reducer() -> Html {
<>
<div id="result">{ counter.counter }</div>
<button onclick=double_onclick>{ "Double" }</button>
<button onclick=square_onclick>{ "Square" }</button>
<button onclick={double_onclick}>{ "Double" }</button>
<button onclick={square_onclick}>{ "Square" }</button>
</>
}
}
@ -191,7 +191,7 @@ fn effect() -> Html {
};
html! {
<button onclick=onclick>{ format!("Increment to {}", counter) }</button>
<button onclick={onclick}>{ format!("Increment to {}", counter) }</button>
}
}
```
@ -237,7 +237,7 @@ pub fn app() -> Html {
html! {
// `ctx` is type `Rc<UseStateHandle<Theme>>` while we need `Theme` so we deref it
// It derefs to `&Theme`, hence the clone
<ContextProvider<Theme> context=(*ctx).clone()>
<ContextProvider<Theme> context={(*ctx).clone()}>
// Every child here and their children will have access to this context.
<Toolbar />
</ContextProvider<Theme>>
@ -262,7 +262,7 @@ pub fn themed_button() -> Html {
let theme = use_context::<Theme>().expect("no ctx found");
html! {
<button style=format!("background: {}; color: {};", theme.background, theme.foreground)>
<button style={format!("background: {}; color: {};", theme.background, theme.foreground)}>
{ "Click me!" }
</button>
}

View File

@ -24,7 +24,7 @@ is that every expression implements `Into<Classes>`.
```rust
html! {
<div class=classes!("container")></div>
<div class={classes!("container")}></div>
}
```
@ -32,7 +32,7 @@ html! {
```rust
html! {
<div class=classes!("class-1", "class-2")></div>
<div class={classes!("class-1", "class-2")}></div>
}
```
@ -42,7 +42,7 @@ html! {
let my_classes = String::from("class-1 class-2");
html! {
<div class=classes!(my_classes)></div>
<div class={classes!(my_classes)}></div>
}
```
@ -50,7 +50,7 @@ html! {
```rust
html! {
<div class=classes!(Some("class")) />
<div class={classes!(Some("class"))} />
}
```
@ -58,7 +58,7 @@ html! {
```rust
html! {
<div class=classes!(vec!["class-1", "class-2"])></div>
<div class={classes!(vec!["class-1", "class-2"])}></div>
}
```
@ -68,7 +68,7 @@ html! {
let my_classes = ["class-1", "class-2"];
html! {
<div class=classes!(&my_classes)></div>
<div class={classes!(&my_classes)}></div>
}
```
@ -104,11 +104,11 @@ impl Component for MyComponent {
} = &self.props;
html! {
<div
class=classes!(
class={classes!(
"my-container-class",
fill.as_some("my-fill-class"),
class.clone(),
)
)}
>
{ children.clone() }
</div>

View File

@ -67,7 +67,7 @@ impl Component for Container {
fn view(&self) -> Html {
html! {
<div id=self.0.id.clone()>
<div id={self.0.id.clone()}>
{ self.0.children.clone() }
</div>
}

View File

@ -68,7 +68,7 @@ boolean expressions can be used:
let no = 1 + 1 != 2;
html! {
<div hidden=no>
<div hidden={no}>
{ "This div is NOT hidden." }
</div>
}
@ -82,15 +82,13 @@ This will result in the following **HTML**:
## Optional attributes for HTML elements
Most HTML attributes can be marked as optional by placing a `?` in front of
the `=` sign. This makes them accept the same type of value as otherwise, but
wrapped in an `Option<T>`:
Most HTML attributes can use optional values (Some(x) or None). This allows us to omit the attribute if the attribute is marked as optional.
```rust
let maybe_id = Some("foobar");
html! {
<div id?=maybe_id></div>
<div id={maybe_id}></div>
}
```
@ -132,7 +130,7 @@ impl Component for MyComponent {
// Create a callback from a component link to handle it in a component
let click_callback = self.link.callback(|_: ClickEvent| Msg::Click);
html! {
<button onclick=click_callback>
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
@ -165,7 +163,7 @@ impl Component for MyComponent {
// Create a callback from a worker to handle it in another context
let click_callback = self.worker.callback(|_: ClickEvent| WorkerMsg::Process);
html! {
<button onclick=click_callback>
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
@ -197,7 +195,7 @@ impl Component for MyComponent {
});
html! {
<button onclick=click_callback>
<button onclick={click_callback}>
{ "Click me!" }
</button>
}

View File

@ -41,7 +41,7 @@ nothing is rendered, and a message is logged to console stating that no route wa
#[function_component(Main)]
fn app() -> Html {
html! {
<Router<Route> render=Router::render(switch) />
<Router<Route> render={Router::render(switch)} />
}
}
@ -53,7 +53,7 @@ fn switch(route: &Route) -> Html {
html! {
<div>
<h1>{ "Secure" }</h1>
<button onclick=callback>{ "Go Home" }</button>
<button onclick={callback}>{ "Go Home" }</button>
</div>
}
},

View File

@ -101,7 +101,7 @@ impl Component for Model {
fn view(&self) -> Html {
html! {
<div>
<button onclick=self.link.callback(|_| Msg::AddOne)>{ "+1" }</button>
<button onclick={self.link.callback(|_| Msg::AddOne)}>{ "+1" }</button>
<p>{ self.value }</p>
</div>
}

View File

@ -55,7 +55,7 @@ impl Component for MyComponent {
fn view(&self) -> Html {
let onclick = self.link.callback(|_| Msg::Click);
html! {
<button onclick=onclick>{ self.props.button_text }</button>
<button onclick={onclick}>{ self.props.button_text }</button>
}
}
}
@ -84,7 +84,7 @@ impl Component for MyComponent {
fn view(&self) -> Html {
html! {
<input ref=self.node_ref.clone() type="text" />
<input ref={self.node_ref.clone()} type="text" />
}
}

View File

@ -16,7 +16,7 @@ self.node_ref = NodeRef::default();
// In view
html! {
<div ref=self.node_ref.clone()></div>
<div ref={self.node_ref.clone()}></div>
}
// In update

View File

@ -111,28 +111,28 @@ html! {
<!--Interpolated-->
```rust
html! {
<div class=format!("{}-container", size)></div>
<div class={format!("{}-container", size)}></div>
}
```
<!--Expression-->
```rust
html! {
<div class=self.classes()></div>
<div class={self.classes()}></div>
}
```
<!--Tuple-->
```rust
html! {
<div class=("class-1", "class-2")></div>
<div class={("class-1", "class-2")}></div>
}
```
<!--Vector-->
```rust
html! {
<div class=vec!["class-1", "class-2"]></div>
<div class={vec!["class-1", "class-2"]}></div>
}
```
<!--END_DOCUSAURUS_CODE_TABS-->
@ -173,7 +173,7 @@ impl Component for MyComponent {
// Create a callback from a component link to handle it in a component
let click_callback = self.link.callback(|_: ClickEvent| Msg::Click);
html! {
<button onclick=click_callback>
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
@ -206,7 +206,7 @@ impl Component for MyComponent {
// Create a callback from a worker to handle it in another context
let click_callback = self.worker.callback(|_: ClickEvent| WorkerMsg::Process);
html! {
<button onclick=click_callback>
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
@ -237,7 +237,7 @@ impl Component for MyComponent {
});
html! {
<button onclick=click_callback>
<button onclick={click_callback}>
{ "Click me!" }
</button>
}

View File

@ -67,7 +67,7 @@ impl Component for Model {
fn view(&self) -> Html {
html! {
<div>
<button onclick=self.link.callback(|_| Msg::AddOne)>{ "+1" }</button>
<button onclick={self.link.callback(|_| Msg::AddOne)}>{ "+1" }</button>
<p>{ self.value }</p>
</div>
}

View File

@ -51,7 +51,7 @@ impl Component for MyComponent {
fn view(&self) -> Html {
let onclick = self.link.callback(|_| Msg::Click);
html! {
<button onclick=onclick>{ self.props.button_text }</button>
<button onclick={onclick}>{ self.props.button_text }</button>
}
}
}
@ -77,7 +77,7 @@ impl Component for MyComponent {
fn view(&self) -> Html {
html! {
<input ref=self.node_ref.clone() type="text" />
<input ref={self.node_ref.clone()} type="text" />
}
}

View File

@ -15,7 +15,7 @@ self.node_ref = NodeRef::default();
// 在 view 中
html! {
<div ref=self.node_ref.clone()></div>
<div ref={self.node_ref.clone()}></div>
}
// 在 update 中

View File

@ -111,28 +111,28 @@ html! {
<!--插值-->
```rust
html! {
<div class=format!("{}-container", size)></div>
<div class={format!("{}-container", size)}></div>
}
```
<!--表达式-->
```rust
html! {
<div class=self.classes()></div>
<div class={self.classes()}></div>
}
```
<!--元组-->
```rust
html! {
<div class=("class-1", "class-2")></div>
<div class={("class-1", "class-2")}></div>
}
```
<!--Vector-->
```rust
html! {
<div class=vec!["class-1", "class-2"]></div>
<div class={vec!["class-1", "class-2"]}></div>
}
```
<!--END_DOCUSAURUS_CODE_TABS-->
@ -172,7 +172,7 @@ impl Component for MyComponent {
// 从组件 link 中创建回调来在组件中处理它
let click_callback = self.link.callback(|_: ClickEvent| Msg::Click);
html! {
<button onclick=click_callback>
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
@ -204,7 +204,7 @@ impl Component for MyComponent {
// 从 worker 中创建回调来在另一个上下文中处理它
let click_callback = self.worker.callback(|_: ClickEvent| WorkerMsg::Process);
html! {
<button onclick=click_callback>
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
@ -235,7 +235,7 @@ impl Component for MyComponent {
});
html! {
<button onclick=click_callback>
<button onclick={click_callback}>
{ "Click me!" }
</button>
}

View File

@ -56,7 +56,7 @@ impl Component for Model {
fn view(&self) -> Html {
html! {
<div>
<button onclick=self.link.callback(|_| Msg::AddOne)>{ "+1" }</button>
<button onclick={self.link.callback(|_| Msg::AddOne)}>{ "+1" }</button>
<p>{ self.value }</p>
</div>
}

View File

@ -51,7 +51,7 @@ impl Component for MyComponent {
fn view(&self) -> Html {
let onclick = self.link.callback(|_| Msg::Click);
html! {
<button onclick=onclick>{ self.props.button_text }</button>
<button onclick={onclick}>{ self.props.button_text }</button>
}
}
}
@ -77,7 +77,7 @@ impl Component for MyComponent {
fn view(&self) -> Html {
html! {
<input ref=self.node_ref.clone() type="text" />
<input ref={self.node_ref.clone()} type="text" />
}
}

View File

@ -18,7 +18,7 @@ self.node_ref = NodeRef::default();
// 在 view 裡
html! {
<div ref=self.node_ref.clone()></div>
<div ref={self.node_ref.clone()}></div>
}
// 更新

View File

@ -111,28 +111,28 @@ html! {
<!--Interpolated-->
```rust
html! {
<div class=format!("{}-container", size)></div>
<div class={format!("{}-container", size)}></div>
}
```
<!--Expression-->
```rust
html! {
<div class=self.classes()></div>
<div class={self.classes()}></div>
}
```
<!--Tuple-->
```rust
html! {
<div class=("class-1", "class-2")></div>
<div class={("class-1", "class-2")}></div>
}
```
<!--Vector-->
```rust
html! {
<div class=vec!["class-1", "class-2"]></div>
<div class={vec!["class-1", "class-2"]}></div>
}
```
<!--END_DOCUSAURUS_CODE_TABS-->
@ -172,7 +172,7 @@ impl Component for MyComponent {
// 從一個元件連結中,建立一個 callback 並在元件中處理他
let click_callback = self.link.callback(|_: ClickEvent| Msg::Click);
html! {
<button onclick=click_callback>
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
@ -204,7 +204,7 @@ impl Component for MyComponent {
// 從一個 worker 中建立一個 callback並在其他的 context 中處理他
let click_callback = self.worker.callback(|_: ClickEvent| WorkerMsg::Process);
html! {
<button onclick=click_callback>
<button onclick={click_callback}>
{ "Click me!" }
</button>
}
@ -235,7 +235,7 @@ impl Component for MyComponent {
});
html! {
<button onclick=click_callback>
<button onclick={click_callback}>
{ "Click me!" }
</button>
}

View File

@ -64,7 +64,7 @@ impl Component for Model {
fn view(&self) -> Html {
html! {
<div>
<button onclick=self.link.callback(|_| Msg::AddOne)>{ "+1" }</button>
<button onclick={self.link.callback(|_| Msg::AddOne)}>{ "+1" }</button>
<p>{ self.value }</p>
</div>
}