Yew: Ergonomic pattern for passing along on* callbacks to children

Created on 29 Aug 2020  路  5Comments  路  Source: yewstack/yew

Describe the feature you'd like
I am in the process of creating a Bulma component library for Yew. For many of the components, users will need to attached callback handlers to respond to events. There are A LOT of different event types, and I'm looking for a way to be able to collect any on* event handlers attached to the component, and pass them down to the component's concrete HTML elements (where the DOM events actually take place).

Is your feature request related to a problem? Please describe. _(Optional)_
The problem is that I would have to create a very large amount of code on almost all of the components to be able to pass along these on* event handlers. Not impossible ... but definitely seems like an anti-pattern.

Describe alternatives you've considered _(Optional)_
I've looked into using https://github.com/deniskolodin/mixin, but the need for a generic type parameter seems to be causing issues with that approach.

Questionnaire

  • [ ] I'm interested in implementing this myself but don't know where to start
  • [ ] I would like to add this feature
  • [x] I don't have time to add this right now, but maybe later

On that last note, if there is no straightforward way to handle this in Yew right now ... I'll probably just postpone the development of the component library :). It is primarily for a personal project, so I can move forward without finishing things.

Either way, I would love to implement this capability in Yew, just need to hear from those of you that know the code base the best on what design options we have here.

feature

Most helpful comment

I'm working on a bootstrap library for Yew right now and stumbled over the same issue. My idea to solve this would be to use - as a way to express the flattening which works kinda well because - is never allowed as Identifier. Each flattened props would require an unique prefix. It would look like this:

#[derive(Clone, Properties)]
struct SizingProps {
  #[prop_or_default]
  pub sm: Option<usize>,
}

#[derive(Clone, Properties)]
struct ThemeProps {
  #[prop_or_default]
  pub text_color: Option<String>,
  #[prop_or_default]
  pub background_color: Option<String>,
}

#[derive(Clone, Properties)]
struct MyListComponentProps {
  pub children: Children,
  #[flatten("tp")]
  pub theme: ThemeProps,
  #[flatten]
  pub sizing: SizingProps,
}

html! {
  <MyListComponent -sm=100 tp-text_color="maybe green or something idk">
    <span>{ "Hello World" }</span>
  </MyListComponent>
}

All 5 comments

That's something I've wanted for a really long time now. Not only for callbacks but for all kinds of props.
I've been thinking about using an approach similar to the serde(flatten) attribute which would allow something like this:

#[derive(Clone, Properties)]
struct ThemeProps {
  #[prop_or_default]
  pub text_color: Option<String>,
  #[prop_or_default]
  pub background_color: Option<String>,
}

#[derive(Clone, Properties)]
struct MyListComponentProps {
  pub children: Children,
  #[props_inherit]
  pub theme: ThemeProps,
}

html! {
  <MyListComponent text_color="maybe green or something idk">
    <span>{ "Hello World" }</span>
  </MyListComponent>
}

This way we can adhere to "composition over inheritance" but still have some syntactic sugar.
What do you think?

@siku2 nice, I like that. Yea, the composition approach would be great. I suppose with an approach like this, it would mostly be modifications to the Properties derive macro? I've done a fair bit of proc-macro/derive macro work, so I'm definitely happy to help out!

Yes, it needs a clever idea to make it work with the html! macro though, because it is completely unaware of the actual Properties struct. One idea I have is to use DerefMut on the builder for this.

Feel free to give this a shot. The relevant code is in the yew-macro directory. The derive macro for Properties can be found in the derive_props module and the part of the html! macro that handles components is in the html_components.rs file.

I'm working on a bootstrap library for Yew right now and stumbled over the same issue. My idea to solve this would be to use - as a way to express the flattening which works kinda well because - is never allowed as Identifier. Each flattened props would require an unique prefix. It would look like this:

#[derive(Clone, Properties)]
struct SizingProps {
  #[prop_or_default]
  pub sm: Option<usize>,
}

#[derive(Clone, Properties)]
struct ThemeProps {
  #[prop_or_default]
  pub text_color: Option<String>,
  #[prop_or_default]
  pub background_color: Option<String>,
}

#[derive(Clone, Properties)]
struct MyListComponentProps {
  pub children: Children,
  #[flatten("tp")]
  pub theme: ThemeProps,
  #[flatten]
  pub sizing: SizingProps,
}

html! {
  <MyListComponent -sm=100 tp-text_color="maybe green or something idk">
    <span>{ "Hello World" }</span>
  </MyListComponent>
}

For the original issue, the proposed syntax examples leave out an important detail: How would a MyListComponent implementation pass on all of the events to a child?
I'd guess something like with could be used:
<div with self.props.events />
But of course it would also need to support specifying other standard HTML attributes alongside that

<div
    class="something"
    with=self.props.events.clone()
    />

and that would also have to work for wrapped components, kinda like ref does.

Just throwing in some thoughts here. I'm currently cherry-picking just the events our internal project needs into yew-mdc, but that makes the list of events look a bit strange from the API perspective, so it would be great to have this at some point.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

FrontMage picture FrontMage  路  4Comments

Boscop picture Boscop  路  5Comments

nixpulvis picture nixpulvis  路  4Comments

sackery picture sackery  路  3Comments

ghost picture ghost  路  4Comments