Skip to content

Instantly share code, notes, and snippets.

@markerikson
Created December 12, 2017 16:10
Show Gist options
  • Save markerikson/33353b696418dce9fba5cf570e51f0ec to your computer and use it in GitHub Desktop.
Save markerikson/33353b696418dce9fba5cf570e51f0ec to your computer and use it in GitHub Desktop.
Backbone / Ampersand compatibility shims
// Ampersand-State doesn't mix in Underscore methods the way Backbone.Model does.
// Technically it could also be done as a standalone mixin, but we'll do that here.
var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit'];
// Mix in each Underscore method as a proxy to `Model#attributes`.
_.each(modelMethods, function(method) {
State.prototype[method] = function() {
var args = [].slice.call(arguments);
args.unshift(this.attributes);
return _[method].apply(_, args);
};
});
// State doesn't include has() like BB.Model - add it in
State.prototype.has = function(attr) {
return this.get(attr) != null;
};
// Override the default toJSON() implementation to allow compatibility with BB.Epoxy
State.prototype.toJSON = function(options) {
var originalData = this.serialize();
// Epoxy passes in "{computed : true}" when it tries to determine what attributes
// are available on a Model instance, since that's what Epoxy.Model uses.
// State, however, uses "derived" instead of "computed", and also has "session" fields.
// This fix allows Epoxy binding to work with State instances that have derived/session attributes.
if(options && options.computed)
{
_.extend(originalData, this.getAttributes({derived: true, session: true}));
}
return originalData;
};
// Add a string to State that we can use to help identify this as a valid Model instance
// when adding to a BB.Collection
State.prototype.modelType = "model";
// State has built-in support for fields containing JS primitives, JS objects, and other State
// instances, but not BB.Collections. Add a new "collection" datatype so that a State containing a
// nested Collection instance will properly bubble up events to the parent State.
State = State.extend({
dataTypes : {
collection : {
"set": function (newVal) {
var isInstance = _.result(newVal, "getType") == "collection";
if (isInstance) {
return {
val: newVal,
type: 'collection'
};
} else {
return {
val: newVal,
type: typeof newVal
};
}
},
compare: function (currentVal, newVal, attributeName) {
var isSame = currentVal === newVal;
// if this has changed we want to also handle
// event propagation
if (!isSame) {
if (currentVal) {
this.stopListening(currentVal);
}
if (newVal != null) {
this.listenTo(newVal, 'all', this._getEventBubblingHandler(attributeName));
}
}
return isSame;
}
}
}
});
return {
State : State
};
// Add hooks for compatibility between Backbone and Ampersand
Backbone.Model.prototype.getType = function() {
return "model";
};
Backbone.Model.prototype.isState = true;
Backbone.Collection.prototype.getType = function() {
return "collection";
};
// Override the default _isModel() function to determine if an item is a valid model
// based on our custom "getType" values rather than the normal "instanceof Model" check.
// This will allow us to store both BB.Models and amp.States with no issues.
Backbone.Collection.prototype._isModel = function(model) {
return _.result(model, "getType") == "model";
};
// amp.State tries to call serialize() on its nested "children" and "collections", so
// add these for compatibility.
Backbone.Model.prototype.serialize = function() {
return this.toJSON();
};
Backbone.Collection.prototype.serialize = function() {
return this.toJSON();
};
Backbone.Model.prototype.getId = function () {
return this[this.idAttribute];
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment