I have 2 models and one collection. JobSummary
is a model, JobSummaryList
is a collection of JobSummary
items, and then I have a JobSummarySnapshot
model that contains a JobSummaryList
:
JobSummary = Backbone.Model.extend({}); JobSummaryList = Backbone.Collection.extend({ model: JobSummary }); JobSummarySnapshot = Backbone.Model.extend({ url: '/JobSummaryList', defaults: { pageNumber: 1, summaryList: new JobSummaryList() } });
When I call fetch
on the JobSummarySnapshot
object, it gets everything... Except when I move through the summaryList
collection they are all of type object
and not JobSummary
.
I suppose this makes sense since other than the defaults
object, it doesn't know that the summaryList
should be of type JobSummaryList
. I can go through each item and convert it to a JobSummary
object, but I was hoping there was a way to do it without having to do it manually.
Here's my test code (working jsfiddle here):
var returnData = { pageNumber: 3, summaryList: [ { id: 5, name: 'name1'}, { id: 6, name: 'name2'} ] }; var fakeserver = sinon.fakeServer.create(); fakeserver.respondWith('GET', '/JobSummaryList', [200, { 'Content-Type': 'application/json'}, JSON.stringify(returnData)]); var callback = sinon.spy(); var summarySnapshot = new JobSummarySnapshot(); summarySnapshot.bind('change', callback); summarySnapshot.fetch(); fakeserver.respond(); var theReturnedList = callback.getCall(0).args[0].attributes.summaryList; _.each(theReturnedList, function(item) { console.log('Original Item: '); console.log(item instanceof JobSummary); // IS FALSE var convertedItem = new JobSummary(item); console.log('converted item: '); console.log(convertedItem instanceof JobSummary); // IS TRUE });
UPDATE: It occurred to me that I could override the parse function and set it that way... I have this now:
JobSummarySnapshot = Backbone.Model.extend({ url: '/JobSummaryList', defaults: { pageNumber: 1, summaryList: new JobSummaryList() }, parse: function(response) { this.set({pageNumber: response.pageNumber}); var summaryList = new JobSummaryList(); summaryList.add(response.summaryList); this.set({summaryList: summaryList}); } });
This works so far. Leaving the question open in case someone has comment on it....
Advertisements. Collections are ordered sets of Models. We just need to extend the backbone's collection class to create our own collection. Any event that is triggered on a model in a collection will also be triggered on the collection directly.
Backbone. Backbone has been around for a long time, but it's still under steady and regular development. It's a good choice if you want a flexible JavaScript framework with a simple model for representing data and getting it into views.
model() You have to override the model property of the collection class to specify the model class that the collection contains. You can pass raw attributes objects to add, create and reset and the attributes will be converted into a model of the proper type.
Your parse()
function shouldn't set()
anything, its a better practice to just return the attributes, Backbone will take care of setting it. e.g.
parse: function(response) { response.summaryList = new JobSummaryList(response.summaryList); return response; }
Whatever you return from parse()
is passed to set()
.
Not returning anything (which is like returning undefined
) is the same as calling set(undefined)
, which could cause it not to pass validation, or some other unexpected results if your custom validate()
/set()
methods expects to get an object. If your validation or set()
method fails because of that, the options.success
callback passed to Backbone.Model#fetch()
won't be called.
Also, to make this more generic, so that set()
ing to a plain object from other places (and not only from the server response) also effects it, you might want to override set()
instead:
set: function(attributes, options) { if (attributes.summaryList !== undefined && !(attributes.summaryList instanceof JobSummaryList)) { attributes.summaryList = new JobSummaryList(attributes.summaryList); } return Backbone.Model.prototype.set.call(this, attributes, options); }
You might also find Backbone-relational interesting - it makes it much easier to deal with collections/models nested inside models.
edit I forgot to return from the set() method, the code is now updated
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With