-
-
Notifications
You must be signed in to change notification settings - Fork 666
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
feat: Allow for a more concise syntax when passing views as props #766
Comments
Would be nice if this was possible (didn't test code) #[component]
fn ResourceOk<R, RV, IV, Props>(
cx: Scope,
resource: ReadSignal<Result<R, String>>,
resource_view: RV,
) -> impl IntoView
where
R: 'static,
RV: FnOnce(Scope, Props) -> IV + 'static,
Props: From<R>,
IV: IntoView,
{
todo!()
} So you would be able to call it by: struct User;
#[component]
fn UserProfile(cx: Scope, user: User) -> impl IntoView { }
#[component]
fn SomeComponent(cx: Scope) -> impl IntoView {
let (name, set_name) = create_signal(cx, Ok(User {}));
view! { cx,
<ResourceOk resource=name resource_view=UserProfile />
} Update: added working example Example codeuse leptos::*;
use std::marker::PhantomData;
#[component]
fn ResourceOk<R, RV, IV, Props>(
cx: Scope,
resource: ReadSignal<Result<R, String>>,
view: RV,
#[prop(default = Default::default())] _m1: PhantomData<IV>,
#[prop(default = Default::default())] _m2: PhantomData<Props>,
) -> impl IntoView
where
R: Clone + 'static,
RV: Fn(Scope, Props) -> IV + Sized + 'static,
Props: From<R>,
IV: IntoView,
{
move || match resource() {
Err(_) => view! { cx, <></> },
Ok(r) => view! { cx, <>{view(cx, r.into())}</> },
}
}
impl From<User> for UserProfileProps {
fn from(user: User) -> Self {
UserProfileProps { user }
}
}
#[derive(Clone)]
struct User {
name: String,
}
#[component]
fn UserProfile(cx: Scope, user: User) -> impl IntoView {
view! { cx, <div>"User profile: "{user.name}</div>}
}
#[component]
fn App(cx: Scope) -> impl IntoView {
let (name, set_name) = create_signal(
cx,
Ok(User {
name: "Bram".to_owned(),
}),
);
view! { cx,
<>
<button on:click=move |_| set_name.set(Ok(User { name: "Someone".to_owned()}))>"Change name"</button>
<button on:click=move |_| set_name.set(Err("Something bad happened".to_owned()))>"Error"</button>
<ResourceOk resource=name view=UserProfile/>
</>
}
}
fn main() {
mount_to_body(|cx| view! { cx, <App/> })
} |
Another area this might apply to is when using views in code that's already inside a let values = vec![0, 1, 2];
view! { cx,
<ul>
{values.into_iter()
.map(|n| view! { cx, <li>{n}</li>})
.collect::<Vec<_>>()}
</ul>
} Allow for something like this: let values = vec![0, 1, 2];
view! { cx,
<ul>
{values.into_iter()
.map(|n| <li>{n}</li>) // <-- Using `<li>{n}</li>` directly without the nested `view!` macro.
.collect::<Vec<_>>()}
</ul>
} So maybe instead of this feature only being about passing views as props, it should be about being able to use the HTML-like view syntax anywhere inside a Taking the above example even further (although this may be going beyond the scope of the original issue), it would also be nice to be able to use iterators as children, so that we could also leave off the let values = vec![0, 1, 2];
view! { cx,
<ul>
{values.into_iter().map(|n| <li>{n}</li>)}
</ul>
} Also, I'm very new to Rust, so feel free to let me know if I'm making any incorrect assumptions about what would be possible. |
This is not really feasible, considering that the |
I am not sure that having syntax like this is a good idea: <Routes>
<Route path="/" view=<Home/>/>
<Route path="/other" view=<Other>"children"</Other>/>
<Route path="/*any" view=<>"just text"</>/>
</Routes>
view! { cx,
<ul>
{values.into_iter()
.map(|n| <li>{n}</li>) // <-- Using `<li>{n}</li>` directly without the nested `view!` macro.
.collect::<Vec<_>>()}
</ul>
} This one is probably impossible as @bram209 have mentioned. But what have you considered making both construction more declarative? For example: <Routes>
<Route path="/" > <Home/> </Route>
<Route path="/other" /> <Other>"children"</Other> </Route>
<Route path="/*any" />"just text"</Route>
</Routes> it can become more verbose, because of closed tag, but also more html-like? For nested routes, we can treat single child as a path to route, force to use special <Routes>
<Route path="/" >
<Home/>
<Route path="/*any" />"just text"</Route>
</Route>
<Route path="/other" />
<Route sdefault>
<Other>"children"</Other>
</Route>
<Route path="/*any" />{"just text"}</Route>
</Route>
</Routes> with nested <Routes>
<Route path="/" >
<Home/>
<Routes>
<Route path="/*any" />"just text"</Route>
<Routes/>
</Route>
</Routes> Or more complex example with
|
We could probably use the new <Routes>
<Route path="/" >
<Home/>
<Route slot:nested path="/*any" />"just text"</Route>
</Route>
<Route path="/other" />
<Route>
<Other>"children"</Other>
</Route>
<Route slot:nested path="/*any" />{"just text"}</Route>
</Route>
</Routes> Although I think I actually find that less readable than the |
This looks less readable for me too. |
Some good news on this one. First off, something like this already works if you just leave off <Routes>
<Route path="/" view=Home/>
<Route path="/other" view=Other/>
</Routes > ie But granted that's not ideal. Components by default are redefined into functions with two arguments, Scope and their props. But I did a little experimenting and I think I can get the component macro to define no-prop components as one-argument functions (ie ones that just take Scope) without breaking the |
So #1144 implements the idea I mentioned, such that you can write things like view! { cx,
<Route
path=""
view=ContactList
>
<Route
path=":id"
view=Contact
/>
<Route
path="/"
view=|cx| view! { cx, <p>"Select a contact."</p> }
/>
</Route>
} instead of view! { cx,
<Route
path=""
view=|cx| view! { cx, <ContactList/> }
>
<Route
path=":id"
view=|cx| view! { cx, <Contact/> }
/>
<Route
path="/"
view=|cx| view! { cx, <p>"Select a contact."</p> }
/>
</Route>
} Thinking more about the feedback re: parsing from @vldm and thinking about the way it works in React's JSX, I think directly passing additional components as attributes is probably not the right direction. Note that in JSX, you'd do this inside curly braces <Something prop={<SomethingElse/>}/> which is the equivalent of view! { cx,
<Something prop={view! { cx, <SomethingElse/> }}/> (just that we explicitly invoke a macro rather than automatically transforming the syntax. I think this probably ends up in a happy medium place, and I'm going to consider this closed once #1144 is merged, unless there are any objections. See also the binding syntax introduced in #1140, which makes it easier to pass children that takes args instead of render props. |
Instead of having to write this:
Allow for something like this:
Would it be possible for the
view!
macro to support this type of syntax?The text was updated successfully, but these errors were encountered: