Skip to content

Instantly share code, notes, and snippets.

@tkambler
Created January 20, 2014 16:32
Show Gist options
  • Save tkambler/8523452 to your computer and use it in GitHub Desktop.
Save tkambler/8523452 to your computer and use it in GitHub Desktop.
Patterns for implementing client-side JS models and collections with Knockout and Falcon
var User = BaseModel.extend({
'url': 'user.json',
'defaults': {
'first_name': null,
'last_name': null,
'email': null
}
});
var Users = BaseCollection.extend({
'model': User,
'initialize': function() {
BaseCollection.prototype.initialize.apply(this);
}
});
var users = new Users();
users.fetch().done(function() {
/*
Reference the underlying observableArray behind `users` at: users.models
*/
users.each(function(user) {
/*
Model attributes are observables and can be referenced lik so:
user.first_name
*/
/*
Reference 'unwrapped' attributes like so:
user.get('first_name');
*/
/*
Update attributes like so:
user.set('first_name', 'Joe');
*/
});
});
define(['falcon', 'underscore', 'knockout'], function(Falcon, _, ko) {
var BaseCollection = Falcon.Collection.extend({
'initialize': function() {
this.createDefaultEvents();
},
'createDefaultEvents': function() {
var self = this;
this.on('sync', function() {
_.each(self.models(), function(model) {
model.trigger('sync');
});
});
},
'fetch': function(obj) {
var self = this;
obj = obj || {};
var success;
if ( _.isFunction(obj.success) ) {
success = obj.success;
}
obj.success = function() {
self.trigger('sync');
if ( _.isFunction(success) ) {
success();
}
}
return Falcon.Collection.prototype.fetch.call(this, obj);
},
'subscribe': function() {
return this.models.subscribe.apply(this.models, _.toArray(arguments));
}
});
return BaseCollection;
});
define(['falcon', 'knockout'], function(Falcon, ko) {
var BaseModel = Falcon.Model.extend({
'initialize': function(data) {
var self = this;
if ( !_.isObject(this.defaults) ) {
this.defaults = {};
}
this.createCollections();
this.createObservables();
Falcon.Model.prototype.initialize.apply(this, _.toArray(arguments));
_.defer(function() {
self.trigger('ready');
});
},
'fill': function(data) {
var self = this;
this.populateCollections(data);
var stripped_data = {};
_.each(data, function(value, key) {
if ( key === 'id' || !_.isUndefined(self.defaults[key]) ) {
stripped_data[key] = value;
}
});
return Falcon.Model.prototype.fill.call(this, stripped_data);
},
'populateCollections': function(data) {
var self = this;
_.each(data, function(value, key) {
if ( _.isArray(value) && Falcon.isCollection(self[key]) ) {
self[key].fill(value);
delete data[key];
}
});
},
'collections': {},
'createCollections': function() {
var self = this;
_.each(this.collections, function(Collection, key) {
self[key] = new Collection([], self);
});
},
'createObservables': function() {
var self = this;
_.each(this.defaults, function(value, key) {
if ( value === '' || _.isNull(value) ) {
value = undefined;
}
self['_' + key] = ko.observable(value);
self[key] = ko.computed({
'read': function() {
if ( self.getters[key] ) {
return self.getters[key](self['_' + key]());
} else {
return self['_' + key]();
}
},
'write': function(value) {
self['_' + key](value);
},
'owner': self
});
self[key].subscribe(function(value) {
self.trigger('change:' + key, value);
});
});
},
'fetch': function(obj) {
var self = this;
obj = obj || {};
var success;
if ( _.isFunction(obj.success) ) {
success = obj.success;
}
obj.success = function() {
self.trigger('sync');
if ( _.isFunction(success) ) {
success();
}
}
return Falcon.Model.prototype.fetch.call(this, obj);
},
'parse': function(data) {
return data;
var self = this;
var result = [];
_.each(data, function(row, index) {
var new_row = {};
_.each(row, function(value, key) {
if ( _.isArray(value) ) {
/* This array represents a collection belonging to this object. */
if ( Falcon.isCollection(self[key]) ) {
self[key].fill(value);
}
} else {
/* This is a standard key / value pair. */
new_row[key] = value;
}
});
result.push(new_row);
});
return result;
},
'serialize': function() {
var self = this;
var result = {
'id': this.id
};
_.each(this.defaults, function(value, key) {
result[key] = self.get(key);
});
_.each(this.collections, function(collection, key) {
result[key] = self[key].serialize();
});
return result;
},
'getters': {}
});
return BaseModel;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment