In my application, I am trying to use Firebase to store the real time data based on backbone framework.
The problem goes like this:
I have a sub level model and collection, which are both general backbone model and collection.
var Todo = Backbone.Model.extend({
defaults: {
title: "New Todo",
completed : true
}
});
var Todocollection = Backbone.Collection.extend({
model: Todo,
initialize: function() {
console.log("creating a todo collection...");
},
});
And then there is a high level model, which contains the sublevel collection as an attribute.
var Daymodel = Backbone.Model.extend({
defaults : {
day: 1,
agenda : new Todocollection()
}
});
and then for the higher level collection, I will firebase collection
var DayCollection = Backbone.Firebase.Collection.extend({
model: Daymodel
});
So far I can add data to the higher level collection correctly, which has a day
attribute and an agenda
attribute (which should be a TodoCollection
).
The issue is when I try to add data to the sub-level collections, it can't work well.
this.collection.last()
.get("agenda")
.add({
title: this.input.val(),
completed: false
});
The above code will be inside the View part. And this.collection.last()
will get the last model. get("agenda")
should be the collection object.
But it can't work. The error shows that this.collection.last(...).get(...).add
is not a function.
After debugging I found that this.collection.last().get("agenda")
returns a general JS object instead of collection object.
I further debugged that if I use backbone collection as the outer collection DayCollection
. Everything can go well.
How to solve such problem?
Why the default collection attribute is not a collection anymore?
When you fetch, or create a new
Daymodel
which I assume looks like this:The default
agenda
attribute which was aTodocollection
at first gets replaced by a raw array of objects. Backbone doesn't know thatagenda
is a collection and won't automagically populates it.This is what Backbone does with the
defaults
at model creation (line 401):_.extend({}, defaults, attrs)
puts thedefaults
first, but then, they're overwritten by the passedattrs
.How to use a collection within a model?
Below are three solutions to accomplish this. Use only one of them, or create your own based on the followings.
Easiest and most efficient way is don't.
Keep the
Todocollection
out of theDaymodel
model and only create the collection when you need it, like in the hypotheticalDayView
:Then, when there are changes you want to persist in the model, you just put the collection models back into the
Daymodel
:Put the collection into a property of the model
Instead of an attribute, you could make a function which lazily create the collection and keeps it inside the model as a property, leaving the
attributes
hash clean.Then, the model controls the collection and it can be shared easily with everything that shares the model already, creating only one collection per instance.
When saving the model, you still need to pass the raw models back into the
attributes
hash.A collection inside the attributes
You can accomplish what you're already trying to do with small changes.
Never put objects into the
defaults
...without using a function returning an object instead.
Otherwise, the
agenda
collection would be shared between every instances ofDaymodel
as the collection is created only once when creating theDaymodel
class.This also applies to object literals, arrays, functions (why would you put that in the
defaults
anyway?!).Ensure it's always a collection.
Ensure it's serializable.
This could easily apply if you put the collection in a model property as explained above.
Avoid accidentally overriding the
agenda
attribute.This goes alongside with point 2 and that's where it's getting hard as it's easy to overlook, or someone else (or another lib) could do that down the line.
It's possible to override the
save
andset
function to add checks, but it gets overly complex without much gain in the long run.What's the cons of collection in models?
I talked about avoiding it inside a model completely, or lazily creating it. That's because it can get really slow if you instantiate a lot of models and slower if each models is nested multiple times (models which have a collection of models, which have other collections of models, etc).
When creating it on demand, you only use the machine resources when you need it and only for what's needed. Any model that's not on screen now for example, won't get their collection created.
Out of the box solutions
Maybe it's too much work to get this working correctly, so a complete solution might help and there are a couple.