DioxusLabs / dioxus

Fullstack app framework for web, desktop, mobile, and more.
https://dioxuslabs.com
Apache License 2.0
21.34k stars 823 forks source link

Beginner confusion: Docs fix? Can't call methods inside rsx! { ... } ? #3073

Open tgrushka opened 2 weeks ago

tgrushka commented 2 weeks ago

(Is it a) Problem (/ Rust limitation?)

The following works (awesome!):


#[derive(Clone, strum::EnumIter, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
enum Route {
    #[route("/")]
    Home {},
    #[route("/counter")]
    Counter {},
    #[route("/blog/:id")]
    Blog { id: i32 },
}

impl Route {
    fn icon(&self) -> String {
        match self {
            Route::Home {} => "home".into(),
            Route::Counter {} => "add_circle".into(),
            Route::Blog { .. } => "blog".into(),
        }
    }

    fn title(&self) -> String {
        match self {
            Route::Home {} => "Home".into(),
            Route::Counter {} => "Counter".into(),
            Route::Blog { .. } => "Blog".into(),
        }
    }
}

#[component]
fn RailDrawerNav() -> Element {
    let nav_buttons = Route::iter().map(|route| {
        let icon = route.icon();
        let title = route.title();
        rsx! {
          Link { to: route,
            i { "{icon}" }
            div { "{title}" }
          }
        }
    });
    rsx! {
      nav { class: "left drawer l",
        header {
          nav {
            img {
              src: "https://www.beercss.com/favicon.png",
              class: "circle"
            }
            h6 { "Cheers" }
          }
        }
        for button in nav_buttons {
          { button }
        }
        div { class: "divider" }
        a {
          i { "search" }
          div { "Search" }
        }
        a {
          i { "share" }
          div { "Share" }
        }
        a {
          i { "more_vert" }
          div { "More" }
        }
      }
    }
}

#[component]
fn Blog(id: i32) -> Element {
    rsx! {
      RailDrawerNav {}
      // ...
    }
}

But the following does not work (why does the analyzer show an irrelevant missing trailing comma error?):


#[component]
fn RailDrawerNav() -> Element {
    rsx! {
      nav { class: "left drawer l",
        header {
          nav {
            img {
              src: "https://www.beercss.com/favicon.png",
              class: "circle"
            }
            h6 { "Cheers" }
          }
        }
        for route in Route::iter() {
          Link { to: route,
            i { route.icon() } // missing trailing comma rust-analyzer[macro-error]
            div { route.title() }
          }
        }
        // ...
      }
      // ...
    }
}

Was hoping to do that, but then it became only slightly clearer when I found this compiles, but yields improper output because it's interpreting icon and title as attribute keys, not values:

let nav_buttons = Route::iter().map(|route| {
    let icon = route.icon();
    let title = route.title();
    rsx! {
      Link { to: route,
        i { icon }
        div { title }
      }
    }
});

Steps To Reproduce

Try the above code snippets.

Expected behavior

It is likely expected. However, it would be helpful to have:

  1. Better tutorial docs on dealing with things like properties / methods of things. If I had a say, this would be a high priority as it should be an easy fix for the maintainers knowing how it should work. Just a quick example section would be great, because it's probably a very common stumbling block for newcomers.
  2. A better analyzer error (not trailing comma) -- I saw another issue that suggests this might be in the works but don't know if related. Probably much harder fix, knowing Rust macro analyzer limitations.

Screenshots

Unwanted render trying the last method above (unquoted var name as node content, i.e. div { title }):

image

Correct render with quoted var names, i.e. div { "{title}" }

image

Environment:

Questionnaire

I would like to help fix this by helping with documentation but I'm brand new to this framework so I'm not sure of the recommended way to deal with this issue. If I knew, would be happy to write up a PR for the docs.

tgrushka commented 2 weeks ago

Wait 2 milliseconds!

I think I found it (wrap method calls in extra { ... }:

#[component]
fn RailDrawerNav() -> Element {
    rsx! {
      nav { class: "left drawer l",
        header {
          nav {
            img {
              src: "https://www.beercss.com/favicon.png",
              class: "circle"
            }
            h6 { "Cheers" }
          }
        }
        for route in Route::iter() {
          Link { to: route.clone(),
            i { { route.icon() } }
            div { { route.title() } }
          }
        }
// ...

Would be happy to contribute a docs PR.

ealmloff commented 1 week ago

Dioxus rsx is similar to rust structs, if you have a plain ident with the name of an attribute, then it set the attribute (like Foo { bar } sets the fieled bar to the value bar). This is documented here: 1) In the rsx reference: https://dioxuslabs.com/learn/0.5/reference/rsx#expressions 2) In the (new 0.6 only) rsx inline docs: https://docs.rs/dioxus/0.6.0-alpha.3/dioxus/prelude/macro.rsx.html#raw-expressions 3) In the (new 0.6 only) examples alongside element: https://docs.rs/dioxus-html/0.6.0-alpha.3/dioxus_html/elements/div/index.html#usage-in-rsx

I think we can improve the macro error message for that parsing case. It would be helpful to hear what documentation you have read before running into this issue so that we know where cross links need to be added. There are some plans to expand the guide in https://github.com/DioxusLabs/docsite/pull/277