I would like to save calls to my server, so I am currently using Model.save()
with the patch
option and sending changedAttributes()
.
I would like to remove an attribute and add a new one. Model.set()
/unset()
will modify changedAttributes()
each time such that I cannot use it with the Model.save()
/patch
scheme described above.
I think I would like to simply call Model.set()
and pass in an object with the values I wish to unset set to undefined
along with the values I wish to set.
Is there a way that I can unset()
and set()
in one go to get the changedAttributes()
? Or maybe determine the changedAttributes()
for a combined set of operations?
// Currently
var m = new Backbone.Model({ "foo": "bar" });
m.unset("foo");
console.log(m.changedAttributes()); // { "foo": undefined }
m.set("baz", "bar");
console.log(m.changedAttributes()); // { "baz": "bar" }
console.log(m.attributes); // { "baz": "bar" }
// At this point, how do I get the combination of changed attributes? something like: { "foo": undefined, "baz": "bar" }?
// Is that even possible? Am I doing something horribly wrong?
//================================================================
// What (I think) I want is for Model.set() to remove attributes with values of undefined, so I only have to make one call and changedAttributes() will be pristine. Maybe with a option or something?
var w = new Backbone.Model({ "foo": "bar" });
w.set({ "foo": undefined, "baz": "bar" });
console.log(w.changedAttributes()); // { "foo": undefined, "baz": "bar" }
console.log(w.attributes); // I would like it to be { "baz": "bar" }, "foo" having been removed in the set() call.
//================================================================
// I was trying to avoid processing the objects by hand. I realize that I can do something like the following.
var h = new Backbone.Model({ "foo": "bar" });
var changes = { "foo": undefined, "baz": "bar" };
_.each(changes, function(val, key) {
if (_.isUndefined(val)) {
h.unset(key, { "silent": true });
} else {
h.set(key, val, { "silent": true });
}
});
h.trigger('change'); // Trigger a change event after all the changes have been done.
console.log(changes); // { "foo": undefined, "baz": "bar" }
console.log(h.attributes); // { "baz": "bar" }
Fiddle of above code in action: http://jsfiddle.net/clayzermk1/AmBfh/
There seems to have been some discussion on this topic about a year ago https://github.com/documentcloud/backbone/pull/879. It seems like the functionality I wanted existed at some point.
EDIT: As @dennis-rongo pointed out, I can obviously do this by hand. To restate my question above: "Does Backbone allow setting/deleting of attributes at once?" and if not, what is the rationale behind that decision? Derick Bailey created Backbone.Memento (https://github.com/derickbailey/backbone.memento) to deal with attribute states, and there are several issues on Backbone about model states closely related to this scenario (https://github.com/documentcloud/backbone/pull/2360, somewhat relevant: https://github.com/documentcloud/backbone/issues/2316, highly relevant: https://github.com/documentcloud/backbone/issues/2301).
EDIT 2: I'm not looking for a hand-rolled solution, I can make it do more or less what I want (see sample code above). I'm looking for a justification of the current design with a clean example for this common scenario - set and unset in one go.
UPDATE: There has been some conversation about this subject in https://github.com/documentcloud/backbone/issues/2301. I have submitted a pull request (https://github.com/documentcloud/backbone/pull/2368) to try and encourage discussion of the current implementation.
Thank you to everyone who posted an answer!
There's a lot of ways to skin this one! So, I'll focus on your the part of your question where you ask:
Is there a way that I can
unset()
andset()
in one go to get thechangedAttributes()
?
because I think that's the way to go here.
Backbone.Model.unset()
is just an alias for Backbone.Model.set()
. From the source:
unset: function(attr, options) {
return this.set(attr, void 0, _.extend({}, options, {unset: true}));
},
So why not just do m.set({"baz": "bar", "foo": void 0});
? See this fiddle I forked from yours: http://jsfiddle.net/dimadima/Q8ZuV/. Pasting from there, the result will be
console.log(m.changedAttributes()); // { "baz": "bar", "foo": undefined }
console.log(m.attributes); // // {foo: undefined, baz: "bar"}, unfortunately "foo"is not deleted
So m.attributes
is a bit off because the key you've unset hasn't been deleted, but you can test for that.
Anyway, I recommend skimming the source of Backbone.Model.set()
to get a sense of what your other options would be. I could elaborate if you'd like.