Skip to content

Instantly share code, notes, and snippets.

@yurtaev
Created April 18, 2012 12:49
Show Gist options
  • Save yurtaev/2413383 to your computer and use it in GitHub Desktop.
Save yurtaev/2413383 to your computer and use it in GitHub Desktop.
Support nested id's for backbone.js Model
(function(Backbone){
// Helper function to accessing nested JavaScript objects with string key
// via. http://stackoverflow.com/a/6491621
var byString = function(object, stringKey) {
stringKey = stringKey.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
stringKey = stringKey.replace(/^\./, ''); // strip a leading dot
var a = stringKey.split('.');
while (a.length) {
var n = a.shift();
if (n in object) {
object = object[n];
} else {
return;
}
}
return object;
};
// Helper function to test the existence of multiple levels
// via. http://stackoverflow.com/a/2631198
var checkNested = function(obj, args) {
for (var i = 0; i < args.length; i++) {
if (!obj.hasOwnProperty(args[i])) {
return false;
}
obj = obj[args[i]];
}
return true;
};
ModelNestedID = Backbone.Model.extend({
set: function(key, value, options) {
var attrs, attr, val;
// Handle both `"key", value` and `{key: value}` -style arguments.
if (_.isObject(key) || key == null) {
attrs = key;
options = value;
} else {
attrs = {};
attrs[key] = value;
}
// Extract attributes and options.
options || (options = {});
if (!attrs) return this;
if (attrs instanceof Backbone.Model) attrs = attrs.attributes;
if (options.unset) for (attr in attrs) attrs[attr] = void 0;
// Run validation.
if (!this._validate(attrs, options)) return false;
// Check for changes of `id`.
if (checkNested(attrs, this.idAttribute.split('.'))) this.id = byString(attrs, this.idAttribute);
var changes = options.changes = {};
var now = this.attributes;
var escaped = this._escapedAttributes;
var prev = this._previousAttributes || {};
// For each `set` attribute...
for (attr in attrs) {
val = attrs[attr];
// If the new and current value differ, record the change.
if (!_.isEqual(now[attr], val) || (options.unset && _.has(now, attr))) {
delete escaped[attr];
(options.silent ? this._silent : changes)[attr] = true;
}
// Update or delete the current value.
options.unset ? delete now[attr] : now[attr] = val;
// If the new and previous value differ, record the change. If not,
// then remove changes for this attribute.
if (!_.isEqual(prev[attr], val) || (_.has(now, attr) != _.has(prev, attr))) {
this.changed[attr] = val;
if (!options.silent) this._pending[attr] = true;
} else {
delete this.changed[attr];
delete this._pending[attr];
}
}
// Fire the `"change"` events.
if (!options.silent) this.change(options);
return this;
}
});
Backbone.Model = ModelNestedID;
})(Backbone);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment