-
Notifications
You must be signed in to change notification settings - Fork 396
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
define event handlers in Ractive.extend() #360
Comments
In regards to the discussion from #357, I like the idea of having methods directly invoked by name. Maybe this could happen when |
Good ideas. Yes, it is quite magical but I'd welcome these features too. For the first unresolved question I think class Two should overwrite class One's event handler. And in the second question, if I want to call both methods, then I recommend to use One = Ractive.extend({
on: {
foo: doFooOneWay
}
});
Two = One.extend({
on: {
foo: function() {
this._super(); // calls foo() of parent class
this.doFooAnotherWay();
}
}
}); And your other question regarding firing events without method names: this.on('showOptionsMenu', this.showOptionsMenu); vs this.on('showOptionsMenu'); // also calls this.showOptionsMenu(); is a nice idea. Why not? Just make sure this is well-documented. |
Thanks @martypdx and @binarykitchen for the input. The Despite having been the one who initially suggested it, I found that something felt not-quite-right about automatically calling methods that have the same names as events, beyond the potential for surprise, and I eventually pinned it down: when you create a method, you're defining an interface for the external world, and so that method should have a signature that is designed accordingly. But event handlers don't have that signature - the first argument is always an internally-generated So if we had something like this... <div class='gallery'>
{{#images:id}}
<img src='{{thumbnail}}' on-click='show:{{id}}'>
{{/images}}
</div> Gallery = Ractive.extend({
show: function (event, id) {
// some code happens
}
}); ..and we wanted to programmatically show a particular image (e.g. as soon as the page has loaded), then we'd need to write some contorted code: gallery.show(null, 'trollface');
// meanwhile, the show() method can't use the `event` object So Arguably, this is just persnickitiness (I realise that many event handlers, e.g. the Gallery = Ractive.extend({
show: function (id) {
// some code happens
},
on: {
show: function (event, id) {
this.show(id);
}
}
}); ...is clearly longer, but it feels more hygienic and less brittle. Or maybe that's just me? |
Hmmm, good thoughts. No, this is not a persnicketiness, but a red flag indeed. I never thought of this. The event object must be passed whenever an event happened. After a second thought I agree with @codler that it's too magical. I think I wouldn't automatically call methods that have the same names as events. Too risky. And that discussion should be in a new ticket (again)! Let's stick to the main topic here, inheriting event handlers from the parent :) |
An idea is to introduce "eventHook" that would intercept all events, I imagine it could look something like this
A common use case for me with the properties are to pass the metods to the template.
I could have set directly to For the question 1 I would say yes, it should inherit events. |
For question 2, I'd choose:
So it's like how event handlers work and you can break the event chain, too. |
For question 2, since it is using .extend, I'do go with overwriting the initial event method. Having _.super() is nice, but adds complexity (what if i'm on the nth extend?). If it didn't use .extend, chaining the calls ala DOM events, (with preventPropagation, etc) would be a good pattern. |
For question 1. I think it should have both events. For question 2. At first I thought it should be overwritten (and we can call |
For those interested, I wrote down some thoughts on this issue over on #405. The tl;dr is that I'm no longer convinced this is a desirable change - it introduces too much ambiguity and the implementation gets rather complex. |
If we follow the basic OOP principles, we'll be fine I think. nth extends are possible in most programming languages and frameworks. So why not here too? It is up to the programmer if (s)he adds complexity or not. |
@binarykitchen Options are a bit different then just normal extensions. For registries, it's geared toward combinatorial extension by key. That is, combining supplied keys for a given registry with those higher in the extension stack given precedence. For example: var Widget1 = Ractive.extend({
partials: {
key1: 'abc',
key2: 'xyz'
}
})
var Widget2 = Widget1.extend({
partials: function(partials, data) {
this._super(partials)
if (data.whatever) {
partials.key2 = '123';
}
partials.key3 = '456';
}
})
var r = new Widget2({
partials: {
key3: 'foo',
key4: 'bar'
},
data: { whatever: true }
})
// r.partials are key1='abc', key2='123', key3='foo', key4='bar' The end result is a hashmap of partials, with ultimately only one value for a given key for any ractive instance. For handlers, "one per key" is probably not what is intended: var Widget1 = Ractive.extend({
on: {
thing1: fn1,
thing2: fn2
}
})
var r = new Widget2({
on: {
thing2: fn3,
thing3: fn4
}
})
// for thing2 does r.on call:
// 1) fn2 and fn3?
// 2) only fn3 and maybe fn2 if fn3 calls this._super?
// 3) something else? |
Good examples. I agree with the first one, hashmap of partials. Nicely presented. Regarding the second example, |
@binarykitchen that's exactly one of those problematic things. Since registered event handlers are stored as a list rather than a hashmap (because you can have more handlers for the same event), we are basically merging two arrays. In JS that means something like |
Oh I see. Getting to know better the internals of RactiveJS ... okay, let me think about it. |
Probably it's a design flaw and we have to introduce breaking changes by following basic OOP rules. Meaning if you want to keep multiple handlers for the same event and extend the class, you must call |
I wonder why @Rich-Harris designed it that way? For simplicity's sake? |
Well, pretty much all the pub/sub systems I know are designed that way. You really want to be able to attach multiple listeners for the same event (in the same Ractive instance). The problem comes with the inheritance as pub/sub systems usually don't allow anything like that. From my POV, if this was about to be implemented, than keeping both fn2 and fn3 is the right thing to do. But than again, I think it's better not to implement this at all. |
I realize this issue is closed, but I just came to a new idea here that's worth mentioning. The original proposed syntax is below: Ractive.extend({
observe: {
"name": function () { ... }
"email": function () { ... }
},
on: {
"refresh": function () { ... }
}
}) It feels natural, but indeed we run into the edge case of not making it clear how to handle multiple handlers per event. For comparison, Backbone.js has an Backbone.View.extend({
events: {
"click .refresh": "updateNewsfeed"
},
updateNewsfeed: function () { ... }
}); Ember allows you to make methods that you tag with Ember.View.extend({
updateNewsfeed: function () {
}.on('refresh')
}); The common pattern here is that they make you define methods, and you just define which methods are associated per event/property. Taking inspiration from this, we can make a better proposal on how to do this in Ractive: Dashboard = Ractive.extend({
events: {
"refresh": "updateNewsfeed",
"dismiss": ["showExitNotification", "closeNewsfeed"]
},
updateNewsfeed: function () { ... },
showExitNotification: function () { ... },
closeNewsfeed: function () { ... }
}) This has the bonus benefit of making it clear how to handle subclassing: if you define more events on a subclass of |
@rstacruz 👍 I proposed that very thing here and I still think it's a decent idea and good to know there's precedent in other libraries. I do think that we are missing the 90% sweet spot of just accepting functions. So I think: new Ractive({
on: {
refresh: function(){ ... }
}
}) should do exactly what you expect it would do. And string methods, and arrays, are just a more advanced case: new Ractive({
on: {
refresh: ['refresh', function(){ ... }]
},
refresh: function(){ ... }
}) IMO, I would stick with |
👍 on all those points. Backbone can also accept functions in those On Friday, September 26, 2014, Marty Nelson [email protected]
|
Despite it's closed, I'd like to vote +1 for this feature. I'd also be very happy with the "light version" of it, I.e. without the super and all that inheritance fluff. I think the "light version" where it simply adds declarations is the most practical and covers the vast majority of use cases. The inheritance within event handlers, to me, looks like YAGNI. For the initial questions:
My 2 cents. ;) (...and I'd be happy about it) |
I agree, will try and do a PR this release (or someone else can take a stab 😸 ) |
+1 from me. Using methods hooks to define events are not a very nice API. Another idea would be to use the extended class to define its events using a "on" class method. It would be consistent with how you do it in instances: var MyView = Ractive.extend({});
MyView.on("eventName", handler); The only problem is that classes would be mutable after declaration. So the |
See #357.
Basically, many components defined with
Ractive.extend()
have this sort of thing:This is fine except that in practice it can end up with duplicated code and other evils. And if you subclass
MyView
, and that subclass has aninit()
method, thefoo
handler won't be bound unless you callthis._super()
, except that maybe that's not what you want becauseMyView.prototype.init
actually contains a whole load of otherMyView
specific init code that the subclass doesn't need, and frankly it can all get a bit messy.If instead we had...
then we'd have neater control of things. In fact,
Ractive.extend()
is a red herring - this may as well be an initialisation option as well as an extend option.Unresolved questions
1.
In the following example...
...should instances of
Two
have bothfoo
andbar
handlers bound? Intuitively it seems they ought to, especially sincetransitions
andpartials
and so on accumulate in this manner.2.
Should event handlers overwrite handlers of the same event further up the chain, or should they too accumulate? I.e....
...should
Two
instances call bothdoFooOneWay
anddoFooAnotherWay
, or justdoFooAnotherWay
? The second option would, I suspect, be less surprising, and it would make it possible to 'null out' event handlers that a subclass didn't need. But at the same time it's contrary to the way event handlers ordinarily work (i.e. you can add as many as you like to the same event). I lean towards the first but would be glad to hear opinions.The text was updated successfully, but these errors were encountered: