Reputation: 5155
In the following code,
this.listenTo(this.model, 'change:voteCount', this.changeVoteCount);
this.listenTo(this.model, 'change', this.renderTemplate);
If "voteCount
" attribute updated, I want to trigger "this.changeVoteCount
" method but not trigger "this.renderTemplate
".
I already googling about 2 hours and couldn't find solution. How can I do for this requirement?
(just the explanation for why I choose @try-catch-finally answer)
For people who also meet this problem, I want to clear out the confusion which answer should be chosen as their solution.
I will describe some details to explain why I choose this answer. The following is my model structure:
{
'name': 'some string',
'location': 'some string',
'link': 'some string',
'voteCount': 0 }
Otherwise 'voteCount
' attribute updated, I want to trigger "change" event binded method (in my case, this.renderTemplate
).
So I tried the first approach,
this.listenTo(this.model, 'change:name change:location change:link', this.renderTemplate);
In this approach, if I update 'location
' and 'name
' at once in one place, then the this.renderTemplate
method will trigger two times. I want this.renderTemplate
method trigger just one time whenever 'location
', 'name
' or 'link
' attributes updated separately or together at the same time.
So finally, I tried the following approach and succeed.
this.listenTo(this.model, 'change', function(model) {
if (_.isEmpty(_.intersection(_.keys(model.changed), ["voteCount"]))) {
this.renderTemplate.apply(this, arguments);
}
});
Upvotes: 1
Views: 161
Reputation: 2624
If you don't want render on every parameter change, you can specify which of them should trigger render.
this.listenTo(this.model, 'change:param1 change:param2', this.renderTemplate);
Edit: if you don't want to render multiple times on multiple fields change, you can use underscore's debounce:
this.listenTo(this.model, 'change:param1 change:param2', _.debounce(this.renderTemplate));
It will call render only once if it will be called several times in a short time.
Upvotes: 2
Reputation: 7634
Beside listing all attributes you want to listen for their change mentioned by @antejan, you could do this programatically too.
This way you'd examine the model's changed
attribute which contains all attributes that have been changed.
this.listenTo(this.model, 'change', function(model) {
if (!_.contains(_.keys(model.changed), "voteCount") {
this.renderTemplate.apply(this, arguments);
}
});
When blacklisting multiple keys you could also do:
if (_.isEmpty(_.intersection(_.keys(model.changed), ["attr1", "attr2", ...])) { ...
As long as your model does only have a few attributes I'd prefer @antejans way of listing all attributes explicitly since its more clear whats going on:
this.listenTo(this.model, "change:attr1 change:attr2 ...", this.renderTemplate);
Note that there might be a performance penalty when listing multiple change:attr
events since Backbone will call the listeners in a for
-loop. See the annotated Backbone source of set()
:
...
if (!silent) {
if (changes.length) this._pending = options;
for (var i = 0, l = changes.length; i < l; i++) {
this.trigger('change:' + changes[i], this, current[changes[i]], options);
}
}
// You might be wondering why there's a `while` loop here. Changes can
// be recursively nested within `"change"` events.
if (changing) return this;
if (!silent) {
while (this._pending) {
options = this._pending;
this._pending = false;
this.trigger('change', this, options);
}
}
Upvotes: 2