Skip to content

Instantly share code, notes, and snippets.

@th3hunt
Last active April 26, 2016 19:33
Show Gist options
  • Save th3hunt/05da2bb6b6cef7153f807035029637cd to your computer and use it in GitHub Desktop.
Save th3hunt/05da2bb6b6cef7153f807035029637cd to your computer and use it in GitHub Desktop.
A mixin that enables special kind of computed properties on backbone models
/**
* Backbone Computed
* -----------------
*
* a mixin that enables special kind of computed properties on backbone models
*
*
*/
(function (root, factory) {
if (typeof exports !== 'undefined') {
module.exports = factory(require('backbone'), require('underscore'));
} else {
factory(root.Backbone, root._);
}
}(this, function (Backbone, _) {
var Computed = {
/**
* Create an attribute that gets computed from the selected model of a Backbone.Select.One collection
*
* @param {string} attributeName - the attribute name
* @param {Backbone.Collection} collection - a Backbone.Select.One collection
* @param {object} [options] - the compute options
* @param {boolean} [options.bidirectional=false] - whether any direct change to the attribute will be reflected on the collection
* @param {*} [options.noneValue=null] - the attribute value for when no model is selected
* @param {string|function} [options.map='id'] - map the the selected model to a value
* @param {string} [options.selectOn='sync reset'] - collection events on which to set the selected model based on the computed attribute
*
* selectOn option is specially useful when the model is initialized with a value for the computed attribute,
* and the collection is then loaded asynchronously. In general, it lets us select a model on the collection
* when the collection emits one or more events of our choosing.
*
* @example
* this.computeFromSelected('job_title', this.collection, {noneValue: 'All Jobs', map: 'title'});
*
*/
computeFromSelected: function (attributeName, collection, options) {
var defaults = {
bidirectional: false,
map: 'id',
noneValue: null,
selectOn: 'sync reset'
};
options = _.defaults(options || {}, defaults);
function modelToValue(model) {
try {
return _.isString(options.map) ? model.get(options.map) : options.map(model);
} catch (e) {
return model.id;
}
}
function compute() {
var model = collection.selected;
this.set(attributeName, model ? modelToValue(model) : options.noneValue);
}
function reverse() {
var model = collection.find(function (m) {
return modelToValue(m) === this.get(attributeName);
}.bind(this));
if (model) {
collection.select(model);
}
}
this.listenTo(collection, 'deselect:one select:one', compute);
if (options.selectOn) {
this.listenTo(collection, options.selectOn, reverse);
}
if (options.bidirectional) {
this.listenTo(this, 'change:' + attributeName, reverse);
}
}
};
_.extend(Backbone.Model.prototype, Computed);
return Computed;
}));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment