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

Nested Components #128

Closed
nixpulvis opened this issue Feb 2, 2018 · 3 comments
Closed

Nested Components #128

nixpulvis opened this issue Feb 2, 2018 · 3 comments

Comments

@nixpulvis
Copy link

Pardon this issue if this is possible, and I just don't know what I'm doing.

I'm trying to setup the following set of components.

// Top level component (not a huge fan of the name "Model").
struct Model;

struct Navigation {
    links: Vec<Link>
}

struct Link {
    title: String,
    path: String,  // I'm actually panning on using http::uri::PathAndQuery
}

impl Component<Context> for Model { ... }
impl Component<Context> for Navigation {
    fn view(&self) -> Html<Context, Self> {
        let render = |link: &Link| html! {
            <li>{ link.view() }</li>
        };
        html! {
            <ul>
                { for self.links.iter().map(render) }
            </ul>
        }
    }
}
impl Component<Context> for Link { ... }

impl Renderable<Context, Self> for Model { ... }
impl Renderable<Context, Self> for Navigation { ... }
impl Renderable<Context, Self> for Link { ... }

I've been playing around with various modifications to this basic outline, and I can't seem to find anything that makes sense and works. Two main issues seem to crop up.

  1. Rendering another component's view inside your own view function seems to be a problem.
  2. It's unclear how to use the create constructor to actually get an instance of the Link, since it requires an Env specific to it.

Sorry if this isn't super clear, I'm happy to provide more information as to my use case.

@therustmonk
Copy link
Member

Hi, @nixpulvis! Sorry for the delay, I have a crazy work schedule )

  1. Rendering another component's view inside your own view function seems to be a problem.

I've found there are two issues about the Renderable, but actually this trait has Conponent restriction not without reason. It makes possible to declare the environment where it could be rendered.
If you want to render Link inside any Component you should describe it in type. For example:

impl Renderable<Context, Self> for Navigation {
    fn view(&self) -> Html<Context, Self> {
        let render = |link: &Link| html! {
            <li>{ link.view() }</li>
        };
        html! {
            <ul>
                { for self.links.iter().map(render) }
            </ul>
        }
    }
}

impl<T> Renderable<Context, T> for Link
where
    T: Component<Context>,
{
    fn view(&self) -> Html<Context, T> {
        html!{
            <a href=self.path.to_owned(),>{ &self.title }</a>
        }
    }
}

Why it's important. Because you could want to have a component which renders in different ways when it laid inside different type parents. Example:

impl Renderable<Context, VerticalNavigation> for Link
{
    fn view(&self) -> Html<Context, T> { ... }
}
impl Renderable<Context, HorizontalNavigation> for Link
{
    fn view(&self) -> Html<Context, T> { ... }
}

Or you could want to use link for both: Navigation and Model:

impl Renderable<Context, Navigation> for Link
{
    fn view(&self) -> Html<Context, Navigation> {
        html!{
            <li>
                <a href=self.path.to_owned(),>{ &self.title }</a>
            </li>
        }
    }
}

impl Renderable<Context, Model> for Link
{
    fn view(&self) -> Html<Context, Model> {
        html!{
            <a href=self.path.to_owned(),>{ &self.title }</a>
        }
    }
}

Link doesn't even have to be a Component.

  1. It's unclear how to use the create constructor to actually get an instance of the Link, since it requires an Env specific to it.

create couldn't get an instance of self, because it have to create it itself. Component uses create to generate an instance of the struct during first rendering when properties exists already. It's impossible to set properties directly now, because the Component's instance lives in a separate box/loop.

I continue to improve the framework and I planned to support children of components in templates. It will help to resolve this issue simpler.

@huangjj27
Copy link
Contributor

As in the game of life example, I try to implement a Renderable trait of Celllule for Model:

impl Renderable<Model> for Cellule {
    fn view(&self) -> Html<Model> {
        let life_status = if self.alive() { "cellule-live" } else { "cellule-dead" };

        html! {
            <div class=("game-cellule", life_status),
                onclick=|_| CellMsg::Toggle,> </div>
        }
    }
}

In this example where CellMsg is a message defined in a separated module cellule, I get some error requiring using Model::Message:

error[E0271]: type mismatch resolving `<Model as yew::html::Component>::Message == cellule::Msg`
   --> src\lib.rs:215:9
    |
215 | /         html! {
216 | |             <div class=("game-cellule", life_status),
217 | |                 onclick=|_| CellMsg::Toggle,> </div>
218 | |         }
    | |_________^ expected enum `Msg`, found enum `cellule::Msg`
    |
    = note: expected type `Msg`
               found type `cellule::Msg`
    = note: required because of the requirements on the impl of `yew::virtual_dom::Listener<Model>` for `yew::html::onclick::Wrapper<[closure@<html_impl macros>:54:15: 54:19]>`
    = note: required for the cast to the object type `yew::virtual_dom::Listener<Model>`
    = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

error: aborting due to previous error
For more information about this error, try `rustc --explain E0271`.
error: Could not compile `game_of_life`.

And I think when we implement a view of an component for other component, it should allow us to use its Message type.

@jstarry
Copy link
Member

jstarry commented Sep 27, 2019

Components have been nestable for awhile now

@jstarry jstarry closed this as completed Sep 27, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants