Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implicit optional attributes #1637

Merged
merged 45 commits into from
Apr 30, 2021
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
7d330aa
start
siku2 Oct 23, 2020
f80566e
update examples
siku2 Oct 27, 2020
198567a
optional attributes for elements
siku2 Oct 27, 2020
e48605e
update macro tests
siku2 Oct 27, 2020
0ed35ff
update yew-stdweb todomvc example
siku2 Oct 27, 2020
5b7ee05
remove quote spanned
siku2 Oct 28, 2020
f6571e1
remove transformer trait
siku2 Oct 28, 2020
f8adf9b
first draft for unified prop handling
siku2 Oct 29, 2020
815147b
update examples
siku2 Oct 29, 2020
ec14013
update optional-attrs
siku2 Nov 9, 2020
692000a
don't even remember to be honest
siku2 Nov 21, 2020
907f05a
update optional-attrs branch
siku2 Nov 22, 2020
f2e6d99
Update optional-attrs
siku2 Nov 25, 2020
0535bdc
remove implicit cloning from strings
siku2 Nov 28, 2020
8de3926
update examples and docs
siku2 Nov 28, 2020
14a237a
update stdweb examples
siku2 Nov 28, 2020
110f789
Merge branch 'v0.18' into optional-attrs
jstarry Jan 24, 2021
57f6ad1
Proper optional attribute for Option<T>
cecton Mar 13, 2021
f062f65
rustfmt
cecton Mar 13, 2021
63d4e0d
Update test stderr (file moved)
cecton Mar 13, 2021
d9c9d52
Fix issues in `WebSocketTask` during connection (#1783) (#1785)
rhymu8354 Mar 12, 2021
dacafc6
Fix issues in `WebSocketTask` during connection (#1783) (#1785)
rhymu8354 Mar 12, 2021
b819ce6
Fix issues in `WebSocketTask` during connection (#1783) (#1785)
rhymu8354 Mar 12, 2021
3c36f3a
Fix fetch test for same-origin referrer URL (#1788)
rhymu8354 Mar 13, 2021
095d5d8
Fixing issue with integration test
cecton Mar 13, 2021
8e4d246
Fix previous commit
cecton Mar 13, 2021
3891794
Fix int test...
cecton Mar 13, 2021
3fd536c
Disable tests for stdweb
cecton Mar 13, 2021
8089f06
hmmhmm
cecton Mar 13, 2021
637136a
hmmhmm
cecton Mar 13, 2021
707c1e7
Be more specific to detect an Option
cecton Mar 20, 2021
d1b9f76
Field became not required
cecton Mar 20, 2021
b317330
Clippy
cecton Mar 20, 2021
45d63a5
Oops this is not public API, we can fix it
cecton Mar 20, 2021
3ee805e
Update documentation
cecton Mar 27, 2021
5f98c06
Revert probable temporary change
cecton Mar 27, 2021
8037e78
Solve some TODOs
cecton Mar 27, 2021
dfe7ccb
Clippy fix
cecton Mar 27, 2021
e488e99
Fix some doc
cecton Mar 27, 2021
af93a08
Update packages/yew-macro/src/derive_props/field.rs
cecton Apr 25, 2021
9728825
Update Rust version for macro tests to 1.51 & enable const generics t…
ranile Mar 30, 2021
390abdb
Update docs/concepts/html/elements.md
cecton Apr 25, 2021
004ef9a
Clippy stuff
cecton Apr 25, 2021
78a3129
Update test stderr for rustc 1.51
cecton Apr 25, 2021
ac09c97
More fix for 1.51.0
cecton Apr 25, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/concepts/function-components/attribute.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub fn rendered_at(props: &RenderedAtProps) -> Html {
html! {
<p>
<b>{ "Rendered at: " }</b>
{ &props.time }
{ props.time.clone() }
</p>
}
}
Expand All @@ -46,7 +46,7 @@ fn app() -> Html {
let counter = Rc::clone(&counter);
Callback::from(move |_| set_counter(*counter + 1))
};

html! {
<div>
<button onclick=onclick>{ "Increment value" }</button>
Expand Down
41 changes: 1 addition & 40 deletions docs/concepts/html/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl Component for Container {

fn view(&self) -> Html {
html! {
<div id=&self.0.id>
<div id=self.0.id.clone()>
{ self.0.children.clone() }
</div>
}
Expand Down Expand Up @@ -112,45 +112,6 @@ impl Component for List {
}
```

## Transformers

Whenever you set a prop its value goes through a transformation step first.
If the value already has the correct type, this step doesn't do anything.
However, transformers can be useful to reduce code repetition.

The following is a list of transformers you should know about:

- `&T` -> `T`

Clones the reference to get an owned value.

- `&str` -> `String`

Allows you to use string literals without adding `.to_owned()` at the end.

- `T` -> `Option<T>`

Wraps the value in `Some`.

```rust
struct Props {
unique_id: Option<usize>,
text: String,
}

struct Model;
impl Component for Model {
type Properties = Props;

// ...
}

// transformers allow you to write this:
html! { <Model unique_id=5 text="literals are fun" /> };
// instead of:
html! { <Model unique_id=Some(5) text="literals are fun".to_owned() /> };
```

Comment on lines -115 to -153
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the entire section here. I added a note about Into<Option<T>> later on.

## Relevant examples
- [Boids](https://github.com/yewstack/yew/tree/master/examples/boids)
- [Router](https://github.com/yewstack/yew/tree/master/examples/router)
Expand Down
20 changes: 15 additions & 5 deletions docs/concepts/html/elements.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,36 @@ let level = 5;
let text = "Hello World!".to_owned()

html! {
<@{format!("h{}", level)} class="title">{ content }</@>
<@{format!("h{}", level)} class="title">{ text }</@>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be better if the tests were executed 😞

}
```

## 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
cecton marked this conversation as resolved.
Show resolved Hide resolved
to omit the attribute if the attribute is marked as optional.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to put things simply. But any advice are welcome 😅


```rust
let maybe_id = Some("foobar");

html! {
<div id?=maybe_id></div>
<div id=maybe_id></div>
}
```

If the attribute is set to `None`, the attribute won't be set in the DOM.

Please note that it is also valid to give only the value as properties behave
like `Into<Option<T>>`:

```rust
let id = "foobar";

html! {
<div id=id></div>
}
```

Comment on lines +37 to +47
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the remark I added about the optional attributes.

## Listeners

Listener attributes need to be passed a `Callback` which is a wrapper around a closure. How you create your callback depends on how you wish your app to react to a listener event:
Expand Down
6 changes: 3 additions & 3 deletions examples/boids/src/slider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,13 @@ impl Component for Slider {

html! {
<div class="slider">
<label for=id class="slider__label">{ label }</label>
<label for=id.clone() class="slider__label">{ label }</label>
<input type="range"
id=id
class="slider__input"
min=min max=max step=step
min=min.to_string() max=max.to_string() step=step.to_string()
oninput=onchange.reform(|data: InputData| data.value.parse().unwrap())
value=value
value=value.to_string()
/>
<span class="slider__value">{ display_value }</span>
</div>
Expand Down
6 changes: 3 additions & 3 deletions examples/crm/src/add_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,19 @@ impl Component for AddClientForm {
<input
class=classes!("new-client", "firstname")
placeholder="First name"
value=&client.first_name
value=client.first_name.clone()
oninput=link.callback(|e: InputData| Msg::UpdateFirstName(e.value))
/>
<input
class=classes!("new-client", "lastname")
placeholder="Last name"
value=&client.last_name
value=client.last_name.clone()
oninput=link.callback(|e: InputData| Msg::UpdateLastName(e.value))
/>
<textarea
class=classes!("new-client", "description")
placeholder="Description"
value=&client.description
value=client.description.clone()
oninput=link.callback(|e: InputData| Msg::UpdateDescription(e.value))
/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion examples/js_callback/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl Component for Model {
<textarea
class="code-block"
oninput=self.link.callback(|input: InputData| Msg::Payload(input.value))
value=&self.payload
value=self.payload.clone()
/>
<button onclick=self.link.callback(|_| Msg::Payload(bindings::get_payload()))>
{ "Get the payload!" }
Expand Down
2 changes: 1 addition & 1 deletion examples/keyed_list/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ 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
value=self.build_component_ratio.to_string()
oninput=self.link.callback(|e: InputData| Msg::ChangeRatio(e.value))
/>
</div>
Expand Down
4 changes: 2 additions & 2 deletions examples/keyed_list/src/person.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ impl PersonType {
}
Self::Component(info) => {
if keyed {
html! { <PersonComponent key=info.id.to_string() info=info /> }
html! { <PersonComponent key=info.id.to_string() info=info.clone() /> }
} else {
html! { <PersonComponent info=info /> }
html! { <PersonComponent info=info.clone() /> }
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion examples/mount_point/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl Component for Model {
html! {
<div>
<input
value=&self.name
value=self.name.clone()
oninput=self.link.callback(|e: InputData| Msg::UpdateName(e.value))
/>
<p>{ self.name.chars().rev().collect::<String>() }</p>
Expand Down
3 changes: 2 additions & 1 deletion examples/nested_list/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::cell::RefCell;
use std::fmt;
use std::ops::Deref;
use std::rc::Rc;
use yew::html::{Component, ComponentLink};
use yew::html::{Component, ComponentLink, ImplicitClone};

pub struct WeakComponentLink<COMP: Component>(Rc<RefCell<Option<ComponentLink<COMP>>>>);

Expand All @@ -16,6 +16,7 @@ impl<COMP: Component> Clone for WeakComponentLink<COMP> {
Self(Rc::clone(&self.0))
}
}
impl<COMP: Component> ImplicitClone for WeakComponentLink<COMP> {}

impl<COMP: Component> Default for WeakComponentLink<COMP> {
fn default() -> Self {
Expand Down
2 changes: 1 addition & 1 deletion examples/router/src/components/author_card.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl Component for AuthorCard {
<div class="media">
<div class="media-left">
<figure class="image is-128x128">
<img src=author.image_url />
<img src=author.image_url.clone() />
</figure>
</div>
<div class="media-content">
Expand Down
2 changes: 1 addition & 1 deletion examples/router/src/components/post_card.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl Component for PostCard {
<div class="card">
<div class="card-image">
<figure class="image is-2by1">
<img src={ &post.image_url } loading="lazy" />
<img src=post.image_url.clone() loading="lazy" />
</figure>
</div>
<div class="card-content">
Expand Down
2 changes: 1 addition & 1 deletion examples/router/src/components/progress_delay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl Component for ProgressDelay {
fn view(&self) -> Html {
let value = self.value;
html! {
<progress class="progress is-primary" value=value max=1.0>
<progress class="progress is-primary" value=value.to_string() max=1.0>
{ format!("{:.0}%", 100.0 * value) }
</progress>
}
Expand Down
2 changes: 1 addition & 1 deletion examples/router/src/pages/author.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 />
<img src=author.image_url.clone() />
</figure>
</div>
<div class="tile is-parent">
Expand Down
2 changes: 1 addition & 1 deletion examples/router/src/pages/author_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl Component for AuthorList {
}

fn view(&self) -> Html {
let authors = self.seeds.iter().map(|seed| {
let authors = self.seeds.iter().map(|&seed| {
html! {
<div class="tile is-parent">
<div class="tile is-child">
Expand Down
6 changes: 3 additions & 3 deletions examples/router/src/pages/post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl Component for Post {
html! {
<>
<section class="hero is-medium is-light has-background">
<img class="hero-background is-transparent" src=post.image_url />
<img class="hero-background is-transparent" src=post.image_url.clone() />
<div class="hero-body">
<div class="container">
<h1 class="title">
Expand Down Expand Up @@ -79,7 +79,7 @@ impl Post {
<article class="media block box my-6">
<figure class="media-left">
<p class="image is-64x64">
<img src=quote.author.image_url loading="lazy" />
<img src=quote.author.image_url.clone() loading="lazy" />
</p>
</figure>
<div class="media-content">
Expand All @@ -99,7 +99,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=section.image_url loading="lazy" />
<img class="hero-background is-transparent" src=section.image_url.clone() loading="lazy" />
<div class="hero-body">
<div class="container">
<h2 class="subtitle">{ &section.title }</h2>
Expand Down
11 changes: 4 additions & 7 deletions examples/router/src/switch.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use yew::{
virtual_dom::{Transformer, VComp},
web_sys::Url,
};
use yew::{html::IntoPropValue, web_sys::Url};
use yew_router::{components::RouterAnchor, prelude::*, switch::Permissive};

#[derive(Clone, Debug, Switch)]
Expand Down Expand Up @@ -79,9 +76,9 @@ impl Switch for PublicUrlSwitch {

// this allows us to pass `AppRoute` to components which take `PublicUrlSwitch`.

impl Transformer<AppRoute, PublicUrlSwitch> for VComp {
fn transform(from: AppRoute) -> PublicUrlSwitch {
from.into_public()
impl IntoPropValue<PublicUrlSwitch> for AppRoute {
fn into_prop_value(self: AppRoute) -> PublicUrlSwitch {
self.into_public()
}
}

Expand Down
2 changes: 1 addition & 1 deletion examples/store/src/post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ impl Component for Post {
<h2>{ format!("Post #{}", self.id) }</h2>
<p>{text}</p>

<TextInput value=text onsubmit=self.link.callback(Msg::UpdateText) />
<TextInput value=text.to_owned() onsubmit=self.link.callback(Msg::UpdateText) />
<button onclick=self.link.callback(|_| Msg::Delete)>
{ "Delete" }
</button>
Expand Down
2 changes: 1 addition & 1 deletion examples/store/src/text_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl Component for TextInput {
html! {
<input
type="text"
value=&self.text
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();
Expand Down
4 changes: 2 additions & 2 deletions examples/todomvc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ impl Model {
<input
class="new-todo"
placeholder="What needs to be done?"
value=&self.state.value
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 }
Expand Down Expand Up @@ -242,7 +242,7 @@ impl Model {
class="edit"
type="text"
ref=self.focus_ref.clone()
value=&self.state.edit_value
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))
Expand Down
2 changes: 1 addition & 1 deletion packages/yew-components/src/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ where
html! {
<select
ref=self.select_ref.clone()
id=self.props.id
id=self.props.id.clone()
class=self.props.class.clone()
disabled=self.props.disabled
onchange=self.on_change()
Expand Down
2 changes: 1 addition & 1 deletion packages/yew-functional/src/hooks/use_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use std::rc::Rc;
///
/// html! {
/// <div>
/// <input onchange=onchange value=message />
/// <input onchange=onchange value=(*message).clone() />
/// <button onclick=onclick>{ "Send" }</button>
/// </div>
/// }
Expand Down
Loading