Integrating raw! macro and seed-quickstart-webpack

Hi there!,

In reference to an earlier post where I’m reading raw html into nodes using the raw!() macro for further processing.

I’m having difficulty matching the return value from raw!() to model.page as it appears in seed-quickstart-webpack: crate/src/lib.rs.

I have modified home.rs to read (and eventually process) the raw html.

pub fn view(model: &Model) -> Vec<Node<Msg>> {
    let nodes: Vec<Node<Msg>> = raw!(model.html);
    nodes
}

Relevant parts of lib.rs

pub struct Model {
    pub html: &'static str,
    ...
}

pub fn view(model: &Model) -> impl IntoNodes<Msg> {
    div![
        ...
        match model.page {
            Page::Home => page::home::view(&model),
            Page::About => page::about::view(),
            Page::NotFound => page::not_found::view(),
        },
        page::partial::header::view(model),
        page::partial::footer::view(),
    ]
}

An error is occurring at Page::Home => page::home::view(&model). As I read it, the problem is that the raw!() macro is returning Vec of Node but the original code is expecting a single Node.

kavik_cz::page::about
pub fn view() -> Node
match arms have incompatible types
expected type std::vec::Vec<seed::virtual_dom::node::Node<_>>
found enum seed::virtual_dom::node::Node<_>rustc(E0308)
lib.rs(182, 9): match arms have incompatible types
lib.rs(183, 27): this is found to be of type

The complete project is here.

home.rs is here.

The relevant lines in lib.rs are here.

Any insight on how to resolve this would be most welcome :slight_smile:

I realize that this issue is really more Rust than Seed and I apologize for that. If it’s a problem, just let me know, and I’ll understand.

You need to transform somehow single nodes into vectors. There are two options:

        match model.page {
            Page::Home => page::home::view(&model),
            Page::About => vec![page::about::view()],
            Page::NotFound => vec![page::not_found::view()],
        },

or

        match model.page {
            Page::Home => page::home::view(&model),
            Page::About => page::about::view().into_nodes(),
            Page::NotFound => page::not_found::view().into_nodes(),
        },

or (if you don’t want to think about return types or it’s possible you’ll be changing them)

        match model.page {
            Page::Home => page::home::view(&model).into_nodes(),
            Page::About => page::about::view().into_nodes(),
            Page::NotFound => page::not_found::view().into_nodes(),
        },

Also there is a macro nodes! that allows you to combine both Node and Vec<Node>:

nodes![
    i_am_node,
    i_am_a_node_vector,
]

It’s useful when you want to return Vec<Node> from a function but you don’t want to wrap individual items in an element or write cumbersome code with vecs and iter().flatten(), etc.


Great! I played around with the various options you provided, they all worked for my case!

Thank you SO Much! :smiley:

I’m wondering, is one preferred over others or is it more or less a matter of preference?

I can see getting into the habit of wrapping all view returns in node![], Is that not a good idea?

example:

pub fn view(model: &Model) -> impl IntoNodes<Msg> {
    nodes![div![
        C![
            IF!(not(model.in_prerendering) => C.fade_in),
            C.min_h_screen,
            C.flex,
            C.flex_col,
        ],
        match model.page {
            Page::Home => page::home::view(&model),
            Page::About => vec![page::about::view()],
            Page::NotFound => page::not_found::view().into_nodes(),
        },
        page::partial::header::view(model),
        page::partial::footer::view(),
    ]]
}

I recommend to write it like:

pub fn view(model: &Model) -> Node<Msg> {
    div![
  • The most readable (I think).
  • Faster - no unnecessary calls and macro expansions.
  • Compiler can’t resolve nested impl XX in some cases => sooner or later you will be forced to define type explicitly.
  • Working with impl IntoNodes or unnecessary node vectors can be tricky once you want to use them inside iterators (e.g. user_names.iter().map(view_user_name) where view_user_name doesn’t return Node<Msg> but impl IntoNodes<Msg>.)
  • IntoNodes / nodes! cause a chain of unnecessary .into_nodes calls in parents.
  • nodes! wrappers would cause unnecessary code block nesting (do you remember callback hells?)

Ad match - I would recommend to write .into_nodes:

        match model.page {
            Page::Home => page::home::view(&model),
            Page::About => page::about::view().into_nodes(),
            Page::NotFound => page::not_found::view().into_nodes(),
        },

A function call should be a little bit better for speed and WASM file size, but the main reason is readability - all arms begin with page::...

Thanks for the great insights. I have bookmarked this thread!

And yes, I (painfully) remember callback hell, along with many additional JS pain points. :wink: