yewstack / yew

Rust / Wasm framework for creating reliable and efficient web applications
https://yew.rs
Apache License 2.0
30.66k stars 1.42k forks source link

Allow use of for loops with `for .. in .. { ... }` syntax in `html!` #1451

Open ranile opened 4 years ago

ranile commented 4 years ago

Describe the feature you'd like Allow using for loops like:

html! {
    for val in range {
      <p> { val } </p>
    }
}

Is your feature request related to a problem? Please describe. (Optional) Current solutions hurt readability when used inside deeply nested html and imo, aren't that concise.

Describe alternatives you've considered (Optional) There are two alternatives to this (mentioned here). Both of which require you to wrap them in html! or empty tags which doesn't read well.

Additional context (Optional) This will also bring loop syntax inside html! in-line with the if statement synatx

Questionnaire

Xavientois commented 3 years ago

What is the if statement syntax you're referring to? Does Yew allow if statement style syntax in the html macro outside of inline rust expressions?

ranile commented 3 years ago

What is the if statement syntax you're referring to?

html! {
    { if condition { something } else { html! {} } }
}

there's no equivalent of it for for loops.

Does Yew allow if statement style syntax in the html macro outside of inline rust expressions?

See "Unanswered questions" in https://github.com/yewstack/yew/issues/1758

allan2 commented 3 years ago

I can see the temptation to add this to make it easier to render collections. After all, Angular and Vue allow for for in their templating.

But iterators are simply the idiomatic way in Rust.

let names = vec!["Bob", "Frank", "Ferris"];

html! {
    for val in names.iter() {
      <p> { val } </p>
}

Rust also has into_iter and inter_mut. Are we going to support them too? Are we going to support if but not the exhaustive match?

You can see where I'm going with this.

Adding for makes it easy to render collections, sure. But it's frustrating when you want to do more. This is why React does not go for this approach. I don't hear many complaints from users about having to use map inside of braces to render a collection. Instead, users are happy that they can leverage the full power of JS in the template.

Rust lets you do even more. The current way of using curly braces is not restricting. It does not judge how you create your iterators, nor how you use them. It is idiomatic Rust. Anything more in html! reduces clarity.

siku2 commented 3 years ago

Thanks for providing your stance on this, @allan2. The following might be a bit direct for which I do apologize, but I wanted to get straight to the point.

Rust also has into_iter and inter_mut. Are we going to support them too?

Iterator loops ("for loops") only care about the IntoIterator trait. There's no need to handle iter, into_iter or iter_mut specially, these functions merely return different types that implement IntoIterator (or in this case just Iterator) with different item types.

Are we going to support if but not the exhaustive match?

You can see where I'm going with this.

I don't really see where you're trying to go with that. for, if, and match, those are the three useful expressions. I don't think that's an unreasonable amount of expressions to support.

Adding for makes it easy to render collections, sure. But it's frustrating when you want to do more.

The entire point is to make it easier to render collections. Can you provide an example of something that would be more frustrating?

Rust lets you do even more. The current way of using curly braces is not restricting. It does not judge how you create your iterators, nor how you use them. It is idiomatic Rust. Anything more in html! reduces clarity.

Idiomatic Rust is hard to define when you're talking about a DSL like Yew's html! macro, but I absolutely get your point. There are cases where using the current syntax is just straight up cleaner, but it's not like the current syntax would disappear.

The benefits of natively supporting a for-loop syntax:

allan2 commented 3 years ago

Thanks for providing your stance on this, @allan2. The following might be a bit direct for which I do apologize, but I wanted to get straight to the point.

@siku2 Not at all. Thank you for your detailed reply.

Iterator loops ("for loops") only care about the IntoIterator trait. There's no need to handle iter, into_iter or iter_mut specially, these functions merely return different types that implement IntoIterator (or in this case just Iterator) with different item types.

Going back to the proposal:

for val in range {
    <p> { val } </p>
}

The user may want to consume while iterating. They may not. I don't think Yew should take an opinion on how the collection is used. The proposal was unclear on how this would be handled.

Are we going to support if but not the exhaustive match? You can see where I'm going with this.

I don't really see where you're trying to go with that. for, if, and match, those are the three useful expressions. I don't think that's an unreasonable amount of expressions to support.

Adding for makes it easy to render collections, sure. But it's frustrating when you want to do more.

The entire point is to make it easier to render collections. Can you provide an example of something that would be more frustrating?

I get what you're saying. I'm on the other side of the fence where I think we shouldn't try to make it easier. It would be at the expense of the ergonomics. I like the React approach where there are no if or for semantics in the template.

Frustrations example: Angular does not allow complex expressions with the ngFor directive. A question was posed on SO: "Why array functions like 'filter' and 'map' doesn't work in 'ngFor' expression of Angular?" (sic). The point was brought up on these restrictions improving change detection. The suggestion was to use the slice pipe, another Angular-specific feature. It's a lot of esoteric knowledge for a task that can already be done in JS. I won't speak to the performance improvement claim but nobody knocks React performance for not having these semantics.

Adding restrictive for semantics does come with advantages, as you suggested in the list at the end of your reply.

React consistently scores at the top for developer experience and Angular... doesn't do so well.

That's my case against adding to the DSL. I've said a lot already :) It's all about ergonomics.

The benefits of natively supporting a for-loop syntax:

  • easy compile-time enforcement of the key attribute.
  • typed components without needing to use html_nested!
  • more aligned with the otherwise declarative style of the macro
  • no recursive macro calls

The technical arguments are worth considering.

By the way, it would be nice if the Yew website docs explained html_nested! in more detail. Could you comment on whether those benefits could be done without the for semantic?

ranile commented 3 years ago

Going back to the proposal:

for val in range {
    <p> { val } </p>
}

The user may want to consume while iterating. They may not. I don't think Yew should take an opinion on how the collection is used. The proposal was unclear on how this would be handled.

I would like to correct myself: I wrote this issue issue when I was very new to Rust. The loop snippet should've been

let iterator = //...
html! {
    for val in iterator {
      <p> { val } </p>
    }
}

The iterator could be drain, into iterator or whatever. The macro would handle this for case similar to how for loops work in Rust