Yew: How to create components by code for example in for loop?

Created on 26 Aug 2020  路  8Comments  路  Source: yewstack/yew


Question

I'm trying to create a Menu component similar to nested_list example. I successfully create them, so creating menus looks good and compiler guaranties, that <Menu> contains only <MenuItem> elements:

html!{
  <Menu>
    <MenuItem>{"item 1"}</MenuItem>
    <MenuItem>{"item 2"}</MenuItem>
    <MenuItem>{"item 3"}</MenuItem>
    //<div /> -- not compiled which is good!
  </Menu>
}

Now I want to compose that Menu component with TreeTable component and add menu that can manipulate visibility of columns. So I need to populate menu based on columns text.

html!{
  <Menu>
    {for self.columns.iter().map(|column| ???)}
  </Menu>
}

That is not compiled, because {for ...} returns Html (aka VNode), and not the MenuItem component. So is there any way to create MenuItem by code like that?

question

All 8 comments

You're looking for the html_nested! macro which works exactly like the html! macro but returns the actual component type instead of Html.

use yew::macros::html_nested;

html! {
  <Menu>
    { for self.columns.iter().map(|column| html_nested! { <MenuItem>{ column }</MenuItem> }) }
  </Menu>
}

Thanks for that macro! I wonder why it's not documented? When this work for single element, unfortunally, this not work for for loops. So this works

html! {
  <Menu>
    { html_nested! { <MenuItem></MenuItem> }
  </Menu>
}

but this not

html! {
  <Menu>
    { for self.columns.iter().map(|column| html_nested! { <MenuItem>{ column }</MenuItem> }) }
  </Menu>
}

and this

html! {
  <Menu>
    {html_nested!{ for self.columns.iter().map(|column| html_nested! { <MenuItem>{ column }</MenuItem> }) }}
  </Menu>
}

I think this has come up before. I'll create an issue calling for it to be documented if there isn't already one.

@Mingun ah, you're right, using for implicitly converts the items to Html too.

This should work:

let items: Vec<MenuItem> = self
    .columns
    .iter()
    .map(|column| html_nested! { <MenuItem>{ column }</MenuItem> })
    .collect();
html! {
  <Menu>
    { items }
  </Menu>
}

@siku2 , thank! With small change thats work. Type should be Vec<VChild<MenuItem>>:

let items: Vec<VChild<MenuItem>> = self
    .columns
    .iter()
    .map(|column| html_nested! { <MenuItem>{ column }</MenuItem> })
    .collect();
html! {
  <Menu>
    { items }
  </Menu>
}

oh right, forget to wrap it in VChild.
FYI, the fact that it wasn't working with { for ... } is a bug, I opened an issue for this here: #1527.

Ok, so I think that this issue can be closed, because there is already a task for documentation, and now also a bug. Thanks for you help!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ghost picture ghost  路  4Comments

DenisKolodin picture DenisKolodin  路  5Comments

sackery picture sackery  路  3Comments

sanpii picture sanpii  路  3Comments

nixpulvis picture nixpulvis  路  4Comments