60

My models already have a defaults hash. When parts of the view/page are reset, I wish to reset the models back to their original defaults.

Currently, I explicitly set each attribute to its default value. Is there anything built in or a JavaScript/Underscore.js/Backbone.js/jQuery function that I could use to do this in a single statement?

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
PhD
  • 11,202
  • 14
  • 64
  • 112

7 Answers7

123
myModel.clear().set(myModel.defaults);
Peter Lyons
  • 142,938
  • 30
  • 279
  • 274
  • Haaaaaaaaaa! Knew it would this simple!!! Thanks :) I can't believe I overlooked this :) – PhD Jul 31 '11 at 17:03
  • 7
    This is kinda dangerous though, because if you didn't specify {id: null} in your defaults, it will have the same id as before and using save() will PUT it to the server, overwriting the old object in the server with default values! – Caleb Hearon Mar 21 '12 at 02:51
  • 3
    If you want a completely different model, construct a new one: `myModel = new MyModel()`. – Peter Lyons Mar 21 '12 at 05:57
  • 5
    True, but it's useful to use the same object if others are bound to it. – Caleb Hearon Mar 21 '12 at 12:27
  • 6
    **Note:** Any properties that are not listed in `defaults` will *not* be cleared from the model. You'll probably want to call [`.clear()`](http://backbonejs.org/#Model-clear) first. – brianreavis Feb 07 '13 at 21:02
  • 1
    One problem I'm having with this approach is that the 'change' event only fires once, before the defaults have been set. – Alasdair McLeay Sep 13 '13 at 15:46
  • 5
    you can use `{silent:true}` as the parameter for the clear to prevent the change event from firing – mix3d Sep 29 '16 at 01:15
6

I came up with the following approach:

reset: function () {
    this.clear({silent: true});
    this.set(this.defaults);
}

Having {silent: true} in the clear() call ensures that the change event is not fired; it will be fire only on set() call.

saabeilin
  • 610
  • 7
  • 15
5

I do this when the model has non-null initial object properties.

first, define defaults as a function

var MyModel = Backbone.Model.extends({

    defaults:function(){

        return {
            AnArrayTypeProp:[]
        };
    }

});

second, when needed to reset model to default

model.clear().set(model.defaults());
codeboy
  • 436
  • 5
  • 9
4

Based on saabeilin's answer and Backbone's annotated source, I came with an optimal function to fill the need for resetting a model.

Reusable reset

/**
 * Clears the model's attributes and sets the default attributes.
 * @param  {Object} attrs to overwrite defaults
 * @param  {Object} options  to pass with the "set" call.
 * @return {Backbone.Model}  this object, to chain function calls.
 */
reset: function(attrs, options) {
    // adds default option reset to true
    options = _.extend({ reset: true }, options);

    // ensure default params
    var defaults = _.result(this, 'defaults');
    attrs = _.defaults(_.extend({}, defaults, attrs), defaults);

    // apply
    this.clear({ silent: true }).set(attrs, options);

    // triggers a custom event, namespaced to model in order
    // to avoid collision with collection's native reset event
    // when listening to a collection.
    if (!options.silent) this.trigger('model:reset', this, options);

    return this;
},

The following line ensures that even if an attribute is passed as undefined { test: undefined }, it'll still have its default value.

attrs = _.defaults(_.extend({}, defaults, attrs), defaults);

Here's an example:

var defaults = { test: 1 }, 
    attrs = { test: undefined };

_.extend({}, defaults, attrs);                       // {test: undefined}
_.defaults(_.extend({}, defaults, attrs), defaults); // {test: 1}

Extending Backbone

And if you want it with all Backbone models, you can extend Backbone itself:

_.extend(Backbone.Model.prototype, {
    reset: function(attributes, options){
        /* ... */
    }
});

Disclaimer: Don't do this if you're writing a JavaScript lib or Backbone plugin, as it could collide with another lib or it could cause a different behavior than the one expected by the person using your code.

Community
  • 1
  • 1
Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
3

I also thought about using model.clear() and model.set() in conjunction. Then I ran across the problem, that I trigger the change event twice now. Using the silent option when calling model.clear() is not an option, because I also want to have a change event fired, when a property gets unset.

I ended up with adding a model.reset() method. It takes a new attributes hash and fills this hash with undefined values for old attributes keys not being present in the new attribute hash.

Model.prototype.reset = function(attrs, options) {
    for (var key in this.attributes) {
        if (key in attrs) continue;
        attrs[key] = void 0;
    }

    return this.set(attrs, options);
};

This way you reset the models old attributes and get a valid change event for every old and new attribute.

Johnny
  • 558
  • 8
  • 20
  • 1
    This code doesn't deal with Model.prototype.changedAttributes() – thesmart May 29 '15 at 22:46
  • To deal with the lack of event when unsetting with `model.clear({silent: true})`, [I trigger a custom event `model:reset`](http://stackoverflow.com/a/36685283/1218980). – Emile Bergeron Jan 19 '17 at 04:19
1

What about overriding the value of current model with a new empty model :

myModel = new model(); // default values will be considered
ismnoiet
  • 4,129
  • 24
  • 30
  • 1
    This works, but it will destroy any events bound to the model. If I have a `change` event on `myModel` and I overwrite it like this, a) it will not fire a change event, and b) any previously bound events would be lost. – dwat Jul 13 '15 at 20:58
1

My solutions is:

model.clear({silent: true}).set(model.defaults);
Daniel Ortegón
  • 315
  • 2
  • 8