Skip to content

Commit

Permalink
Merge 573b8a1 into 61c66c5
Browse files Browse the repository at this point in the history
  • Loading branch information
paulfalgout committed Feb 4, 2015
2 parents 61c66c5 + 573b8a1 commit e0f5b2f
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 37 deletions.
6 changes: 3 additions & 3 deletions docs/marionette.abstractview.md
Expand Up @@ -331,9 +331,9 @@ bound at the time of instantiation, and an exception will be thrown
if the handlers on the view do not exist.

The `modelEvents` and `collectionEvents` will be bound and
unbound with the Backbone.View `delegateEvents` and `undelegateEvents`
method calls. This allows the view to be re-used and have
the model and collection events re-bound.
unbound with the Backbone.View `delegateEntityEvents` and `undelegateEntityEvents`
method calls. `delegateEntityEvents` is called in the View's `constructor` and
entity events are unbound during the View's `destroy`.

### Multiple Callbacks

Expand Down
44 changes: 23 additions & 21 deletions src/abstract-view.js
Expand Up @@ -25,6 +25,8 @@ Marionette.AbstractView = Backbone.View.extend({

Backbone.View.apply(this, arguments);

this.delegateEntityEvents();

Marionette.MonitorDOMRefresh(this);
this.on('show', this.onShowCalled);
},
Expand Down Expand Up @@ -89,23 +91,9 @@ Marionette.AbstractView = Backbone.View.extend({
}, {}, this);
},

// Overriding Backbone.View's delegateEvents to handle
// the `triggers`, `modelEvents`, and `collectionEvents` configuration
delegateEvents: function(events) {
this._delegateDOMEvents(events);
this.bindEntityEvents(this.model, this.getOption('modelEvents'));
this.bindEntityEvents(this.collection, this.getOption('collectionEvents'));

_.each(this._behaviors, function(behavior) {
behavior.bindEntityEvents(this.model, behavior.getOption('modelEvents'));
behavior.bindEntityEvents(this.collection, behavior.getOption('collectionEvents'));
}, this);

return this;
},

// internal method to delegate DOM events and triggers
_delegateDOMEvents: function(eventsArg) {
// Overriding Backbone.View's `delegateEvents` to handle
// `events` and `triggers`
delegateEvents: function(eventsArg) {
var events = Marionette._getValue(eventsArg || this.events, this);

// normalize ui keys
Expand All @@ -123,13 +111,27 @@ Marionette.AbstractView = Backbone.View.extend({
_.extend(combinedEvents, behaviorEvents, events, triggers, behaviorTriggers);

Backbone.View.prototype.delegateEvents.call(this, combinedEvents);

return this;
},

// Overriding Backbone.View's undelegateEvents to handle unbinding
// the `triggers`, `modelEvents`, and `collectionEvents` config
undelegateEvents: function() {
Backbone.View.prototype.undelegateEvents.apply(this, arguments);
// Handle `modelEvents`, and `collectionEvents` configuration
delegateEntityEvents: function() {
this.undelegateEntityEvents();

this.bindEntityEvents(this.model, this.getOption('modelEvents'));
this.bindEntityEvents(this.collection, this.getOption('collectionEvents'));

_.each(this._behaviors, function(behavior) {
behavior.bindEntityEvents(this.model, behavior.getOption('modelEvents'));
behavior.bindEntityEvents(this.collection, behavior.getOption('collectionEvents'));
}, this);

return this;
},

// Handle unbinding `modelEvents`, and `collectionEvents` configuration
undelegateEntityEvents: function() {
this.unbindEntityEvents(this.model, this.getOption('modelEvents'));
this.unbindEntityEvents(this.collection, this.getOption('collectionEvents'));

Expand Down
26 changes: 22 additions & 4 deletions test/unit/behaviors.spec.js
Expand Up @@ -520,16 +520,16 @@ describe('Behaviors', function() {
expect(this.handleCollectionResetStub).to.have.been.calledOnce.and.calledOn(this.fooBehavior);
});

it('should unbind model events on view undelegate', function() {
it('should unbind model events on view undelegateEntityEvents', function() {
this.view = new this.ItemView({ model: this.model });
this.view.undelegateEvents();
this.view.undelegateEntityEvents();
this.model.set('foo', 'doge');
expect(this.handleModelFooChangeStub).not.to.have.been.called;
});

it('should unbind collection events on view undelegate', function() {
it('should unbind collection events on view undelegateEntityEvents', function() {
this.view = new this.CollectionView({ collection: this.collection });
this.view.undelegateEvents();
this.view.undelegateEntityEvents();
this.collection.reset();
expect(this.handleCollectionResetStub).not.to.have.been.called;
});
Expand Down Expand Up @@ -678,6 +678,7 @@ describe('Behaviors', function() {
});

this.sinon.spy(this.view, 'undelegateEvents');
this.sinon.spy(this.view, 'undelegateEntityEvents');
});

it('should call initialize on grouped behaviors', function() {
Expand All @@ -703,6 +704,11 @@ describe('Behaviors', function() {
expect(this.view.undelegateEvents).to.have.been.calledOnce;
});

it('should call undelegateEntityEvents once', function() {
this.view.undelegateEntityEvents();
expect(this.view.undelegateEntityEvents).to.have.been.calledOnce;
});

it('should proxy modelEvents to grouped behaviors', function() {
this.model.trigger('change');
expect(this.barModelChangeStub).to.have.been.calledOnce.and.calledOn(this.barBehavior);
Expand Down Expand Up @@ -767,6 +773,18 @@ describe('Behaviors', function() {
this.view.undelegateEvents({});
expect(this.view.undelegateEvents).to.have.returned(this.view);
});

it('delegateEntityEvents should return the view', function() {
this.sinon.spy(this.view, 'delegateEntityEvents');
this.view.delegateEntityEvents();
expect(this.view.delegateEntityEvents).to.have.returned(this.view);
});

it('undelegateEntityEvents should return the view', function() {
this.sinon.spy(this.view, 'undelegateEntityEvents');
this.view.undelegateEntityEvents({});
expect(this.view.undelegateEntityEvents).to.have.returned(this.view);
});
});

describe('.destroy', function() {
Expand Down
18 changes: 9 additions & 9 deletions test/unit/view.entity-events.spec.js
Expand Up @@ -114,7 +114,7 @@ describe('view entity events', function() {
});
});

describe('when undelegating events on a view', function() {
describe('when undelegating entity events on a view', function() {
beforeEach(function() {
this.View = Marionette.AbstractView.extend({
modelEvents : {'foo': 'foo'},
Expand All @@ -128,8 +128,8 @@ describe('view entity events', function() {
collection : this.collection
});

this.sinon.spy(this.view, 'undelegateEvents');
this.view.undelegateEvents();
this.sinon.spy(this.view, 'undelegateEntityEvents');
this.view.undelegateEntityEvents();

this.model.trigger('foo');
this.collection.trigger('bar');
Expand All @@ -144,7 +144,7 @@ describe('view entity events', function() {
});

it('should return the view', function() {
expect(this.view.undelegateEvents).to.have.returned(this.view);
expect(this.view.undelegateEntityEvents).to.have.returned(this.view);
});
});

Expand All @@ -162,9 +162,9 @@ describe('view entity events', function() {
collection : this.collection
});

this.view.undelegateEvents();
this.sinon.spy(this.view, 'delegateEvents');
this.view.delegateEvents();
this.view.undelegateEntityEvents();
this.sinon.spy(this.view, 'delegateEntityEvents');
this.view.delegateEntityEvents();
});

it('should fire the model event once', function() {
Expand All @@ -177,8 +177,8 @@ describe('view entity events', function() {
expect(this.barStub).to.have.been.calledOnce;
});

it('should return the view from delegateEvents', function() {
expect(this.view.delegateEvents).to.have.returned(this.view);
it('should return the view from delegateEntityEvents', function() {
expect(this.view.delegateEntityEvents).to.have.returned(this.view);
});
});

Expand Down

0 comments on commit e0f5b2f

Please sign in to comment.