Yew: Optional component in properties

Created on 17 Oct 2020  路  6Comments  路  Source: yewstack/yew

Describe the feature you'd like
A clear and concise description of what you want to happen.

Currently a component can have children. However, with JSX you can also set a single component to a property, optionally:

~~~rust

[derive(Clone, PartialEq, Properties)]

pub struct Props {
pub children: ChildrenRenderer,
#[prop_or_default]
pub sidebar: Option,
}
~~~

Is your feature request related to a problem? Please describe. _(Optional)_

Currently you can do this is a list of children, but not with a single one.

You can achieve this currently only using Html:

~~~rust

[derive(Clone, PartialEq, Properties)]

pub struct Props {
pub children: ChildrenRenderer,
#[prop_or_default]
pub sidebar: Html,
}
~~~

However, that doesn't ensure that the component is of a specific (required) type.

Questionnaire

  • [ ] I'm interested in implementing this myself but don't know where to start
  • [ ] I would like to add this feature
  • [X] I don't have time to add this right now, but maybe later
documentation

Most helpful comment

You forgot to convert it to Html. Also, you can't use Option<T> directly.

You're looking for something like this:

html! { self.props.sidebar.clone().map(Html::from).unwrap_or_default() }

EDIT: of course you had to figure it out yourself before I finished my comment :smile:

All 6 comments

This is already possible using VChild<COMP>:

use yew::{
  prelude::*,
  virtual_dom::VChild,
};

struct Sidebar;
impl Component for Sidebar {
  // ...
}

#[derive(Clone, Properties)]
struct Model {
    sidebar: VChild<Sidebar>,
}
impl Component for Model {
    type Properties = Self;

    fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
        props
    }

    // ...

    fn view(&self) -> Html {
        let sidebar = Html::from(self.sidebar.clone());
        html! {
            <>
                <nav>{ sidebar }</nav>
                <main class="my-content">{ ... }</main>
            </>
        }
    }
}

fn render() -> Html {
    // create your beautiful sidebar
    let sidebar = html_nested! { <Sidebar /> };
    html! {
        <Model sidebar=sidebar />
    }
}

We should definitely document this better though!

@siku2 Thanks for the explanation! That should definitely go into the documentation. However it solves my issue only partly, as I am trying to add an optional component.

When use your example, I must provide a sidebar. When I wrap VChild<Sidebar> with Option<>, I get the following error:

~~~
error[E0277]: std::option::Option<yew::virtual_dom::VChild<pagesidebar::PageSidebar>> doesn't implement std::fmt::Display
--> src/page.rs:57:19
|
57 | { self.props.sidebar }
| ^^^^ std::option::Option<yew::virtual_dom::VChild<pagesidebar::PageSidebar>> cannot be formatted with the default formatter
|
= help: the trait std::fmt::Display is not implemented for std::option::Option<yew::virtual_dom::VChild<pagesidebar::PageSidebar>>
= note: in format strings you may be able to use {:?} (or {:#?} for pretty-print) instead
= note: required because of the requirements on the impl of std::string::ToString for std::option::Option<yew::virtual_dom::VChild<pagesidebar::PageSidebar>>
= note: required because of the requirements on the impl of std::convert::From<std::option::Option<yew::virtual_dom::VChild<pagesidebar::PageSidebar>>> for yew::virtual_dom::VNode
= note: required because of the requirements on the impl of std::convert::Into<yew::virtual_dom::VNode> for std::option::Option<yew::virtual_dom::VChild<pagesidebar::PageSidebar>>
= note: required because of the requirements on the impl of std::convert::From<std::option::Option<yew::virtual_dom::VChild<pagesidebar::PageSidebar>>> for yew::utils::NodeSeq<std::option::Option<yew::virtual_dom::VChild<pagesidebar::PageSidebar>>, yew::virtual_dom::VNode>
= note: required by std::convert::From::from
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

~~~

Ok, I was able to work around this using:

~~~rust

[derive(Clone, PartialEq, Properties)]

pub struct Props {
#[prop_or_default]
pub sidebar: Option>,
}

impl Component for Page {
fn view(&self) -> Html {
html! {


{ self.sidebar() }

}
}
}

impl Page {
fn sidebar(&self) -> Html {
match &self.props.sidebar {
Some(sidebar) => Html::from(sidebar.clone()),
None => html! {},
}
}
}
~~~

You forgot to convert it to Html. Also, you can't use Option<T> directly.

You're looking for something like this:

html! { self.props.sidebar.clone().map(Html::from).unwrap_or_default() }

EDIT: of course you had to figure it out yourself before I finished my comment :smile:

You also can inline the html_nested:

~
html!{
sidebar={html_nested!{


}}>

}
~

@siku2 thanks for your help!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sackery picture sackery  路  3Comments

DenisKolodin picture DenisKolodin  路  5Comments

Boscop picture Boscop  路  5Comments

agausmann picture agausmann  路  3Comments

thienpow picture thienpow  路  3Comments