Pauan / rust-dominator

Zero-cost ultra-high-performance declarative DOM library using FRP signals for Rust!
MIT License
967 stars 62 forks source link

Conditional rendering #19

Closed njam closed 4 years ago

njam commented 4 years ago

I'm wondering what is a good way to render dom nodes conditionally, based on a signal.

For example to render different sub pages, based on the current "route" (document.location). In such a case only the current sub page should be added the document, and when the route changes, it should be replaced.

I can use a SignalVec, with each entry representing a different state, and then render only the current state with children_signal_vec(), but it feels like a workaround.

How about adding a function that renders a dom node based on a signal, and adds it as a child to a parent node (here's a draft: https://github.com/Pauan/rust-dominator/pull/18)?
Then it could be used something like this:

html("div")
    .child_signal(state.path.signal_cloned()
        .map({
            let state = Rc::clone(self);
            move |path| {
                match path.as_str() {
                    "/foo" => {
                        html("div").text("foo page!").into_dom()
                    }
                    "/bar" => {
                        html("div").text("bar page!").into_dom()
                    }
                    _ => {
                        html("div").text("error page!").into_dom()
                    }
                }
            }
        })
    )
    .into_dom()

Or maybe there are other, better ways to solve this?

Pauan commented 4 years ago

The correct thing is to use children_signal_vec combined with to_signal_vec, like this:

html("div", {
    .children_signal_vec(state.path.signal_cloned()
        .map({
            let state = Rc::clone(self);
            move |path| {
                match path.as_str() {
                    "/foo" => {
                        vec![
                            html("div", {
                                .text("foo page!")
                            })
                        ]
                    }
                    "/bar" => {
                        vec![
                            html("div", {
                                .text("bar page!")
                            })
                        ]
                    }
                    _ => {
                        vec![
                            html("div", {
                                .text("error page!")
                            })
                        ]
                    }
                }
            }
        })
        .to_signal_vec())
})

Since the signal returns a Vec, this also enables you to return multiple Dom nodes (whenever the signal changes it will remove all the existing children and replace them with the new children).

njam commented 4 years ago

Ah nice! I missed .to_signal_vec(). This solves the problem of course, thanks!