I think I have a pretty simple problem that is just pretty difficult to word and therefore hard to find a solution for. Setup:
- PathCollection is a Backbone.Collection of Paths
- Path is a Backbone.Model which contains NodeCollection (which is a Backbone.Collection) and EdgeCollection (which is a Backbone.Collection).
When I fetch PathCollection
paths = new PathCollection()
paths.fetch()
obviously, Paths get instantiated. However, I'm missing the spot where I can allow a Path to instantiate its submodels from the attribute hashes. I can't really use parse, right? Basically im looking for the entry point for a model when its instantiated and set with attributes. I feel like there must be some convention for it.
So I've written a couple of answers regarding using
parse()
andset()
to instantiate and populate sub-models and sub-collections (nested data). However, I haven't seen a really comprehensive answer that consolidates some of the many practices I've seen. I tend to ramble a bit when I write a lot so I might digress a little but this might be useful for people going through similar kinds of issues.There are a couple ways to do this. Using the
parse()
is one. Manipulating theset()
is another. Instantiating these in yourinitialize()
is another one. Doing it all outside of the Path model is another (e.g.path = new Path(); path.nodes = new NodeCollection();
etc.)A second consideration is this. Do you want the nodes and edge collections to be model attributes? Or model properties?
Oh so many choices. A lot of freedom but sometimes (to our frustration) it makes it more difficult to determine the "right way."
Since this comes up kind of often, I'm going to make a long post and go through these one by one. So bear with me as I continue to update this answer.
Doing it outside of the models - simple and straight-forward
This is usually an easy way to add nested models and collections when you only need it on a particular model or collection.
This is the simplest way and works well when you're dealing with one time models and collections that don't need to have a definition. Although you could easily produce these models in some method (e.g. view method) that constructs this object before doing anything with it.
Using
initialize()
Sub-model / collections in every modelIf you know that every instance of a certain model will always have a sub-model or sub-collection, the easiest way to set things up would be to utilize the
initialize()
function.For example, take your Path model:
Now your sub-collections can be fetched like
path.nodes.fetch()
and it will route to the correct URL. Easy peasy.Using
parse()
for instantiating and setting sub-dataPerhaps, it gets a little more tricky if you don't want to assume every model is going to have a nodes and edge collections. Maybe you want nested models/collections only if the
fetch()
sends back such data. This is the case where usingparse()
can come in handy.The thing with
parse()
is that it takes ANY json server response and can properly namespace and deal with it before passing it to the modelset()
function. So, we can check to see if a model or collection raw data is included and deal with it before reducing the response down to parent model attributes.For example, maybe from our server we get this response:
With the default
parse()
Backbone will gobble this up and assign your path model attributes 'nodes' and 'edge' with an array() of data (not collections.) So we want to make sure that ourparse()
deals with this appropriately.You can also use a customized
set()
to deal with your sub-data. After much going back and forth between which is better, manipulatingset()
or doing it inparse()
I've decided that I like to useparse()
more. But I'm open to other people's thoughts on this.Using
set()
to deal with your sub-dataWhile
parse()
relies on either fetching the data or passing data into a collection with the optionparse:true
some people find it preferential to change theset()
function. Again, I'm not sure there is a correct choice but here is how that would work.So if we already have an attribute and it is a collection, we
reset()
it. If we have an attribute but it isn't a collection, we instantiate it. It's important to make sure you correctly translate the JSON array of sub-data into a collection before you pass it to the prototypeset()
. Backbone, doesn't interpret the JSON array as a collection and you'll only get a straight-up array.So in a nut shell, you got many many options on how to go about. Again, currently I favor a mix of using
initialize()
when I know something will always have those sub-models/collections andparse()
when the situation only calls for possible nested data onfetch()
calls.Regarding that question of yours... (Oh yeah, there was a question)
You can allow Path to instantiate sub-models from a hash in a variety of ways. I just gave you 4. You CAN use parse if you want, if you know you're going to be
fetch()
the path model OR maybe even a pathCollection...pathCollection.fetch({parse:true})
Is there a convention? Maybe maybe not. I like to use a combination of ways depending on the context in which I think I'll be using the models/collections.I'm very open to discussion on some of these practices and whether they are good or bad. They are just many solutions I've come across on Stack and incorporated into my own working habits and they seem to work just fine for me. :-)
Get yourself a coffee and a pat on the back, that was a long read.