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

Variants derive macro #1616

Merged
merged 30 commits into from
Nov 21, 2020
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
cf53d4c
First (untested) implementation of #[derive(Variants)]
K4rakara Oct 12, 2020
19f135a
Add the Variants macro to yew::macros module
K4rakara Oct 12, 2020
1613d1e
Add tests
K4rakara Oct 12, 2020
04ce62b
Add another test
K4rakara Oct 12, 2020
9663200
Make clippy happy
K4rakara Oct 12, 2020
1976eb2
Revert changes
K4rakara Oct 18, 2020
25dca06
First draft
K4rakara Oct 18, 2020
1a82b44
Fix minor typo
K4rakara Oct 18, 2020
357bf1f
Add to sidebars.json
K4rakara Oct 18, 2020
5953450
Fix formatting
K4rakara Oct 18, 2020
ae07b8c
Update docs/concepts/components/children.md
K4rakara Oct 22, 2020
8705d67
Update docs/concepts/components/children.md
K4rakara Oct 22, 2020
6d17ae6
Update docs/concepts/components/children.md
K4rakara Oct 22, 2020
43b974d
Update docs/concepts/components/children.md
K4rakara Oct 22, 2020
4f0e14c
Update docs/concepts/components/children.md
K4rakara Oct 22, 2020
4415677
Update docs/concepts/components/children.md
K4rakara Oct 22, 2020
8d52b7e
Fix indentation in sidebars.json
K4rakara Oct 22, 2020
f02e044
First draft of explination
K4rakara Oct 22, 2020
a629a92
Add suggestion
K4rakara Oct 31, 2020
162d26b
Merge branch 'master' into variants-derive-macro
K4rakara Oct 31, 2020
4c46d53
Quick fixes from review
K4rakara Oct 31, 2020
81b12f9
Quick fixes from review #2
K4rakara Oct 31, 2020
def84aa
Link to the derive_more crate
K4rakara Oct 31, 2020
a0c7572
Switch from tabs to spaces in examples.
K4rakara Oct 31, 2020
405ea75
Inline explination as comments
K4rakara Oct 31, 2020
33c0790
Add back accidentally deleted line
K4rakara Oct 31, 2020
a8eb04f
Merge branch 'master' of https://github.com/yewstack/yew into variant…
K4rakara Nov 14, 2020
4c05399
Update examples :)
K4rakara Nov 14, 2020
03ef7ec
Appease rustfmt
K4rakara Nov 14, 2020
7c0be67
Remove semi-inacurate/confusing section
K4rakara Nov 21, 2020
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
255 changes: 255 additions & 0 deletions docs/concepts/components/children.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
---
title: Children
description:
---

## General usage

_Most of the time,_ when allowing a component to have children, you don't care
what type of children the component has. In such cases, the below example will
suffice.

```rust
use yew::prelude::*;

#[derive(Properties, Clone)]
pub struct ListProps {
#[prop_or_default]
siku2 marked this conversation as resolved.
Show resolved Hide resolved
pub children: Children,
}

pub struct List {
props: ListProps,
}

impl Component for List {
type Properties = ListProps;
// ...

fn view(&self) -> Html {
html! {
<div class="list">
{ for self.props.children.iter() }
</div>
}
}
}
```

## Advanced usage

There are some cases where you wouldn't want to use the `Children` type
and instead use an alternative method:

- You want to only allow components of a specific type to be used as children for your component.

- You want to share state between a component and its children.
K4rakara marked this conversation as resolved.
Show resolved Hide resolved

### Typed children

In cases where you want one type of component to be passed as children to your component,
you can use `yew::html::ChildrenWithProps<T>`.

```rust
use yew::prelude::*;
use yew::html::ChildrenWithProps;

// ...

#[derive(Properties, Clone)]
pub struct ListProps {
#[prop_or_default]
pub children: ChildrenWithProps<Item>,
}

pub struct List {
props: ListProps,
}

impl Component for ListProps {
type Properties = ListProps;
// ...

fn view(&self) -> Html {
html! {
<div class="list">
{ for self.props.children.iter() }
</div>
}
}
}
```

Of course, sometimes you might need to restrict the children to a few different
components. In these cases, you have to get a little more hands-on with Yew.

The `derive_more` crate is used here for better ergonomics. If you don't want
to use it, replace `ItemPropVariants` with this:

```rust
#[derive(Clone)]
pub enum ItemPropVariants {
MyFirstComponent(MyFirstComponentProps),
MySecondComponent(MySecondComponentProps),
}

impl From<MyFirstComponentProps> for ItemPropVariants {
fn from(props: MyFirstComponentProps) -> Self {
Self::MyFirstComponent(props)
}
}

impl From<MySecondComponentProps> for ItemPropVariants {
fn from(props: MySecondComponentProps) -> Self {
Self::MySecondComponent(props)
}
}
```
K4rakara marked this conversation as resolved.
Show resolved Hide resolved

```rust
siku2 marked this conversation as resolved.
Show resolved Hide resolved
use yew::prelude::*;
use yew::html::ChildrenRenderer;
use yew::virtual_dom::{ VChild, VComp };

// ...

#[derive(Clone, derive_more::From)]
pub enum ItemPropVariants {
MyFirstComponent(MyFirstComponentProps),
MySecondComponent(MySecondComponentProps),
}

#[derive(Clone)]
pub struct Item {
props: ItemPropVariants,
}

impl<CHILD> From<VChild<CHILD>> for Item
where
CHILD: Component,
CHILD::Properties: Into<ItemPropVariants>,
{
fn from(vchild: VChild<CHILD>) -> Self {
Item {
K4rakara marked this conversation as resolved.
Show resolved Hide resolved
props: vchild.props.into(),
}
}
}

impl Into<Html> for Item {
fn into(self) -> Html {
match self.props {
ItemPropVariants::MyFirstComponent(props) => {
VComp::new::<MyFirstComponent>(props, NodeRef::default(), None).into()
}
ItemPropVariants::MySecondComponent(props) => {
VComp::new::<MySecondComponent>(props, NodeRef::default(), None).into()
}
K4rakara marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

#[derive(Properties, Clone)]
pub struct ListProps {
#[prop_or_default]
pub children: ChildrenRenderer<Item>,
}

pub struct List {
props: ListProps,
}

impl Component for List {
type Properties = ListProps;
// ...

fn view(&self) -> Html {
html! {
<div class="list">
{ for self.props.children.iter() }
</div>
}
}
}
```
siku2 marked this conversation as resolved.
Show resolved Hide resolved

Heres a run down of whats happening:

This segment here defines the different types of properties that children
components can have. Additionally, it derives `derive_more::From`, which
implements `From<MyFirstComponentProps>` and `From<MySecondComponentProps>` for
us!

```rust
#[derive(Clone, derive_more::From)]
pub enum ItemPropVariants { /* ... */ }
```

Next, we use create this wrapper:
K4rakara marked this conversation as resolved.
Show resolved Hide resolved

```rust
#[derive(Clone)]
pub struct Item {
props: ItemPropVariants,
}
```

Then, we implement `From<VChild<CHILD>>` for our wrapper, where `CHILD`:

- Implements `Component`

- Has a `Self::Properties` value that implements `Into<ItemPropVariants>`.
K4rakara marked this conversation as resolved.
Show resolved Hide resolved

This tells Yew how to handle converting a virtual DOM child into our wrapper!

```rust
impl<CHILD> From<VChild<CHILD>> for Item
where
CHILD: Component,
CHILD::Properties: Into<ItemPropVariants>,
{ /* ... */ }
```

Finally, we implement `Into<Html>` for our wrapper, allowing it to be rendered!

```rust
impl Into<Html> for Item { /* ... */ }
```

### Sharing properties

This use case is mostly helpful for library developers, as you may want to
share state between parent and child components without causing difficulty for
users of your library.

```rust
// ... See the multiple typed children example ...

#[derive(Properties, Clone)]
pub struct ListProps {
#[prop_or_default]
pub children: ChildrenRenderer<Item>,
}

pub struct List {
props: ListProps,
}

impl Component for List {
type Properties = ListProps;
// ...

fn view(&self) -> Html {
html! {
<div class="list">
{
for self.props.children.iter().enumerate().map(|(i, mut item)| {
item.props.value = format!("#{}", i);
item
})
}
</div>
}
}
}
```
3 changes: 0 additions & 3 deletions docs/concepts/components/properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ Types for which you derive `Properties` must also implement `Clone`.

### Field attributes

When deriving `Properties`, all fields are required by default.
The following attributes allow you to give your props initial values which will be used unless they're set to another value.
siku2 marked this conversation as resolved.
Show resolved Hide resolved

:::tip
Attributes aren't visible in Rustdoc generated documentation.
The docstrings of your properties should mention whether a prop is optional and if it has a special default value.
Expand Down
1 change: 1 addition & 0 deletions website/sidebars.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"concepts/components",
"concepts/components/callbacks",
"concepts/components/properties",
"concepts/components/children",
"concepts/components/refs"
]
},
Expand Down