How to detect if an event handler exists


#1

In my controller, I want to send a specific event if a handler exists, and if not, default to a more generic one. Looking at trigger, it looks like the code that looks for handlers is not a separate method, and when not found it throws a string error.

I’m sure catching on the message “Nothing handled the event” is not the best idea.

Is there another way to do this? Or might I suggest the hander lookup be extracted?


#2

Could you give more context into what you’re trying to accomplish with this? One of the nice things about event driven systems is they don’t care about who may be listening to them, allowing for decoupled code that is easier to maintain and reason about. If possible, I’d suggest always sending the generic action, but with extra information as params so that if there’s a specific handler it can use the extra data which the general handler could ignore.


#3

I have a view which sends an action whose result is dependent on the context of the route. For example, let’s say you have a list of tags. When you view tags.index, clicking on a tag in the list opens the show route for that tag. When writing a post, that same tags table appears, but clicking that tag should instead add the tag to the post.

In my tags table, each tag has {{action "selectResource" "tag" this}}. The selectResource function is declared in a subclass of Ember.Route that all of my app’s routes inherit from.

App.BaseRoute = Ember.Route.extend({
  selectResource: function(type, resource) {
    try {
      this.send("select" + type.classify(), resource);
    } catch(err) { // What I'm trying to avoid
      // Action not found
      this.transitionTo(type + ".show", resource);
    }
  };
});

Clicking a tag will first send the event selectTag and if it is not found, it will revert the the default action, a transition to tags.show. I like the idea of having a generic function that handles all resources by default, and then the route would override the more specific function selectTag rather than overriding selectResource and having a switch, calling super, etc.


#4

Does anybody have input on this? I wouldn’t mind catching the error here if the exception were identifiable.


#5

If you really want to go down the road of having one action as the crossroads for two or more actions, I suggest you do what jcollins suggested and pass some more information.

If you are in the template where you want the action to be adding the tag to a post, then put

{{action "selectResource" "tag" this "select"}}

and then make the action be

App.BaseRoute = Ember.Route.extend({
  selectResource: function(type, resource, actionName) {
    if (actionName == 'select') {
      this.send("select" + type.classify(), resource);
    } else {
      // Action not found
      this.transitionTo(type + ".show", resource);
    }
  };
});

Is this sort of on track for what you wanted?

The whole scheme of centralizing the resource clicks like this is pretty clever…perhaps a little too clever for my taste. I would personally make 2n or 3n actions for my n different types of resources. I don’t feel it is too unwieldy, and it makes fine-tuning a lot easier.


#6

Thanks for the example, I see where you’re going. I’m trying to share templates though, so that would mean I’d have to pass down an alternate action name. That feels more coupled to me. I have some other workarounds in mind as well, I’m just not sure yet which one I’m the least unhappy with. They all seem like a compromise.

Actions feel to me like enhanced functions. You get decoupling and bubbling. As with testing for the existence of a function (i.e. an interface) it would seem reasonable to be able to interrogate whether or not an action has a handler. Regardless of that, though, I think anything that can cause an exception should be specifically catchable. Throwing an exception with a string message seems like it was probably an oversight. Or am I missing something?

Bringing overridable, default handling for events seems like a win to me. Certainly with resources, and probably with components too.


#7

Well, as jcollins said, the beauty of an event system like this is that a component can send up an action and the state of the application will determine who hears and reacts to that action first.

So let’s forget about the extra parameter solution I proposed for a second: another way of doing this is to send your action up and have two separate handlers on the two separate controllers.

I know you are reusing the template for the tag table, which is all good, certainly don’t change that. But presumably the template the tag table is embedded in has a different controller in the case where you want it to send the ‘select’ action versus the ‘transition’. That means that the ‘decision’ about which way to go is made by the current state of the application-- which is money from a design perspective.

Example:

{{action "selectTag" this}}
// on controller for tags.index
actions: {
    selectTag: function (tag) {
        // either this.triggerAction('selectResource') for the router, or better yet put the logic right here if it's concise enough
    }
}
// on controller for posts.new
actions: {
    selectTag: function (tag) {
        this.transitionTo('tag.show', tag);
    }
}

I know this and the previous solution I proposed are a bit different from what you have in mind-- so by all means keep going on your way if it’s easier, I’m just throwing out some ways that I think gel well with the way Ember intended this sort of thing to be handled.