ionic-team / stencil

A toolchain for building scalable, enterprise-ready component systems on top of TypeScript and Web Component standards. Stencil components can be distributed natively to React, Angular, Vue, and traditional web developers from a single, framework-agnostic codebase.
https://stenciljs.com
Other
12.56k stars 787 forks source link

There's no way to listen for an event on a single element #323

Closed UserGalileo closed 6 years ago

UserGalileo commented 6 years ago

Stencil version:

 @stencil/core@0.0.8-5

I'm submitting a:

Current behavior:

I'm having some concerns on the behavior of the Listen decorator. It appears it just listens to all child elements' events, which could be useless in most cases. It appears that there's no way to just add an event listener to a single element, a-la Angular/React way. However, I noticed you can actually listen for a single element type, this way: @Listen('my-element:myEvent') But this is not the solution for the problem, because this listens to all elements of type my-element.

Expected behavior:

I think it'd be needed a way to attach an event listener to a single event, to completely remove the need for event namespaces and to increase overall speed. It would be nice to have a template reference to be used with the Listen decorator, or some event handling a-la Angular: <my-element (myEvent)={callback} />

I'm suggesting the Angular's approach over React simple because in Stencil we're using EventEmitters instead of props with callbacks. Thanks!

UserGalileo commented 6 years ago

More about this issue:

1) Currently, when trying to set @Listen('my-component:myEvent') I get a compilation error, running the last version of Stencil: invalid @Listen prefix "my-component" for "my-component:myEvent" So basically, I'm left with namespacing all my events, which is quite boring and error prone.

2) By not having a way to listen on single components, we are obliged to pass indexes into our components if we plan to use a simple map operator, for example:

this.items.map((item, i) => (
    <todo-item item={item} i={i}></todo-item>
))

That's because we can't attach a callback to the single todo-item, of course. This way, the todo-item must emit his index to its parent if we want, for example, to remove it from the parent's array. We are delegating to that component something it shouldn't need to do :)

Thanks.

adamdbradley commented 6 years ago

to add a listener on a specific event you do something like <button onClick={this.myClick.bind(this)}/>.

Hope that helps, thanks

UserGalileo commented 6 years ago

Sorry, that's not what I pointed out. Of course that way you can listen to standard events like click/change/etc, but it doesn't work with custom events created with an @Event() decorator.

If my "clickable-component" has an EventEmitter which emits everytime the user clicks on it, like this: onClick={(e) => { this.clicked.emit(e) }} ...in the parent component this is not possible: <clickable-component clicked={() => console.log("clicked!")}></clickable-component>

Till now I haven't found a way to listen to a specific event on a specific component!

ghost commented 6 years ago

Why is this closed? I have the same issue: custom events only react to child component events. But how do I integrate application wide custom events. Lets say component A is no subcomponent of B but inside B i wan't to react on custom events triggered in A. Cheers

UserGalileo commented 6 years ago

@baerree That's not what I said in this issue though 😄 That particular scenario is the exact opposite of what I meant! This is what is missing:

<my-component (my-custom-event)="doSomething()"></my-component>

ghost commented 6 years ago

Ok then I misunderstood you sorry :) Nevertheless, I am looking for a way to implement some kind of business logic. Like a service in Angular or a Controller in AngularJS. Unfortunately this component approach seems to be limited to vie components. :( therefore I was hoping to create a component without view elements but several listeners to control an application wide state.

UserGalileo commented 6 years ago

Services are something pretty tied to Angular: from a certain point of view, they are dangerous to use as your application state becomes unstable when your app grows. If you have that need, go with a state management library such as Redux or MobX!

ghost commented 6 years ago

Yeah sure there are alternatives. I worked with Angular services and never had the impression you mentioned, but maybe my application wasn't big enough :)

I was hoping stenciljs offers a a way for using business logic as well. But if this is not what they think stencil should be, I am totally fine with it :) Cheers.

sebastien-yoobic commented 6 years ago

Maybe @Listen() could be improve to @Listen('selector:event') where selector is a selector similar to the one in querySelector ?

UserGalileo commented 6 years ago

Imho Listen should be used only for events on body, window etc. We shouldn't even need Listen for this situation :)

jthoms1 commented 6 years ago

This will be in 0.7.6. Releasing right now. You can now use JSX to listen to single events on a component. We think this should solve the problems that people have experienced with the @Listen decorator usage. I would love for people to try this out and provide us with feedback. Thanks

// MyComponent event attribute

@Event() didPresent: EventEmitter<OverlayEventDetail>;
<my-component onDidPresent={(event) => console.log(event)}></my-component>
adamdbradley commented 6 years ago

https://github.com/ionic-team/stencil-site/issues/157

tkiddleYoobic commented 6 years ago

hey im having some issues with this new method, I opened an issue: https://github.com/ionic-team/stencil/issues/662