0

When you create a Backbone model and attach a change event, it will listen for it. And event if you change them at the same time (in the same set call) it will fire the change event the number of changes you have in your model.

So imagine I have a view which is like a grid with filters and it has to refresh every time I change a filter.

My default model looks like this:

defaults: {
    filters: {
        from: '2016-01-01',
        to: today.format(format),
        limit: 25,
        page: 1,
        cycle: 'all'
    }
},

I have a listener for the model in the view:

this.listenTo(this.model, 'change:filters', this.onShow);

And every time I change a filter I do:

this.model.set('filters', {
    from: '2016-07-01',
    to: '2016-07-31',
    limit: 25,
    page: 1,
    cycle: 'currentMonth'
});

As you see I've changed 3 properties of this model. What happen is that it will fire the change event 3 times. What I want to achieve is that it only change once, even if I have 3 changes on the model.

Is that doable?

Thanks in advance

Tomasz Jakub Rup
  • 10,502
  • 7
  • 48
  • 49
Alejandro Garcia Anglada
  • 2,373
  • 1
  • 25
  • 41
  • 2
    What you describe fires the change event only once http://jsfiddle.net/nikoshr/u254vzmb/ Note that you should wrap your `defaults` in a function to avoid sharing the `filters` object http://backbonejs.org/#Model-defaults (the small lines) – nikoshr Aug 09 '16 at 10:22
  • nikoshr is right on all counts. Whatever problem you ran into, it's not the one you describe in your question. Either that, or there's something else going on that you are not showing. – Louis Aug 09 '16 at 16:32

3 Answers3

0

From Backbone documentation:

"change:[attribute]" (model, value, options) — when a specific attribute has been updated.

In Your case model has one attribute: filters. If You set new value to this attribute (no matter it is object, array, int, float, string) event will be fired. Once and only once.

If You need to observe a modification of attribute values, You must write Your own handler.

this.listenTo(this.model, 'change:filters', function(model, value) {
    prev = model.previous('filters');
    if(prev.from != value.from) model.trigger('change:filters:from', model, value.from);
    if(prev.to != value.to) model.trigger('change:filters:to', model, value.to);
    if(prev.limit != value.limit) model.trigger('change:filters:limit', model, value.limit);
    if(prev.page != value.page) model.trigger('change:filters:page', model, value.page);
    if(prev.cycle != value.cycle) model.trigger('change:filters:cycle', model, value.cycle);
});

and in code:

this.listenTo(this.model, 'change:filters:from', this.onShow);
this.listenTo(this.model, 'change:filters:to', this.onShow);
this.listenTo(this.model, 'change:filters:limit', this.onShow);
this.listenTo(this.model, 'change:filters:page', this.onShow);
this.listenTo(this.model, 'change:filters:cycle', this.onShow);
Tomasz Jakub Rup
  • 10,502
  • 7
  • 48
  • 49
0

I've run into this situation mostly when using Marionette's Collection or CompositeViews and listening to changes on the underlying collection. Often times, more than one child model would be updated and the CompositeView would need to be re-rendered or re-ordered, but I didn't want to re-render/re-order for every child model change -- just once when one or more changes occur at the same time. I handled this with a setTimeout like below:

var MyView = Backbone.Marionette.CompositeView.extend({
    childView: ChildView,
    initialize: function() {
        this.listenTo(this.collection, 'change', this.onChildModelChanged);
        this.childModelChanged = false;
    },
    onChildModelChanged: function() {
        // use a setTimeout 0 so that we batch 'changes' and re-render once
        if(this.childModelChanged === false) {
            this.childModelChanged = true;
            setTimeout(function() {
                this.render();
                this.childModelChanged = false;
            }.bind(this), 0);
        }
    }
});

This allows all child model change events to fire before re-rendering or re-ordering. Hope this helps.

lucasjackson
  • 1,515
  • 1
  • 11
  • 21
0

Think this would be the kind of a hack for the scenario in question. You can define a custom event on the model.

  • Listen to a custom event on the model change-filters
  • Pass option silent : true when you are setting the model.
  • Trigger the custom event on the model if there are any changed attributes.
Sushanth --
  • 55,259
  • 9
  • 66
  • 105