Support an events object in Ember Views


#1

Hey guys,

I’m loving everything about Ember so far, but I am missing not being able to set my event delegated handlers using an object (like Backbone).

Like this:

App.ExampleView = Ember.View.extend({
  // This removes the whole event.target ugly checking
  events: {
    '.example click': function() {
      console.log('an example image was clicked!');
    }
  }
});

My thinking is that the delegated events inside of my events object would be relative to my view. Here is a gist I made discussing this further.


#2

Can you provide some examples of what problems you’d solve using this approach? console.log is obviously a weird case.


#3

The obvious reason for supporting this form of event binding is that it gives you the flexibility to just create one view inside of your current template (as opposed to many views). Having one view decreases the complexity of your template considerably (although I am not saying that each template should only have one view obviously). And again, this proposal is not about one particular scenario; just a high-level idea.


#4

You don’t need to create a view for every element that triggers an event. You can use the action helper instead.


#5

Can you use the action helper for events other then click?


#6

Yes, see http://emberjs.com/guides/templates/actions/#toc_specifying-the-type-of-event


#7

Awesome, thanks for the action solution! This will definitely work for now, but it would still be nice to be able to handle all of the logic from the View class instead of having to always add action helpers.


#8

In theory, you could do something with jQuery’s on. For example, you could put the following (untested) code in your view:

didInsertElement: function() {
  this.$().on('click', '.example', function() {
    // Do something
  });
}

Of course, if you went this route, you’d also want to use willRemoveElement to tear down the observer.

In general I would still recommend using {{action}}.


#9

If you placed all of your view dom events inside of an events object property, then Ember could pretty easily remove all events in it’s teardown logic.


#10

Here’s a mixin that you can use:

App.EventedViewMixin = Ember.Mixin.create({
  events: {},

  eachEvent: function(fn) {
    for (var key in this.get('events')) {
      var splitKey = key.split(' ');
      fn.apply(this, [ 
        splitKey.splice(-1).join(), 
        splitKey.join(' '), 
        this.get('events')[key] 
      ]);
    }
  },

  didInsertElement: function() {
    this._super();
    var self = this;
    this.eachEvent(function(eventName, selector, method) {
      this.$().on(eventName, selector, function(e) {
        return method.call(self, e);
      })
    });
  },

  willDestroyElement: function() {
    this._super();
    this.eachEvent(function(eventName, selector) {
      this.$().off(eventName, selector);
    });
  }
});

And then:

App.ExampleView = Ember.View.extend(App.EventedViewMixin, {
  events: {
    '.example click': function() {
      console.log('.example was clicked!');
    }
  }
});

#11

That’s awesome, thanks @teddyzeenny!