Reasons for the Change in Default Collection Attribute
Upon fetching or creating a new Daymodel
, it is noticed that the default agenda
attribute, initially a Todocollection
, has been replaced by a raw array of objects. This change occurs because Backbone does not recognize agenda
as a collection and therefore does not automatically populate it.
When a model is created (as seen in line 401 of backbone.js), the defaults
are established:
var defaults = _.result(this, 'defaults');
attrs = _.defaults(_.extend({}, defaults, attrs), defaults);
this.set(attrs, options);
The _.extend({}, defaults, attrs)
places the defaults
first, but these values are then overwritten by the incoming attrs
.
Implementing a Collection within a Model
Here are three methods to achieve this objective. Choose one approach or devise your own based on the suggestions below.
Optimal Approach: Keep It Separate
To maintain efficiency, avoid embedding the Todocollection
within the Daymodel
. Instead, create the collection only when necessary, such as in a hypothetical DayView
:
var DayView = Backbone.View.extend({
initialize: function() {
// Create the collection directly in the view
this.agenda = new Todocollection(this.model.get('agenda'));
},
/* ...snip... */
});
When changes need to be stored in the model, reintroduce the collection models back into the Daymodel
:
this.model.set('agenda', this.collection.toJSON());
Integrate Collection as a Property
Rather than treating it as an attribute, consider defining a function that dynamically generates the collection within the model as a property, maintaining cleanliness in the attributes
hash:
var Daymodel = Backbone.Model.extend({
defaults: { day: 1, },
getAgenda: function() {
if (!this.agenda) this.agenda = new Todocollection(this.get('agenda'));
return this.agenda;
}
});
Through this approach, the model retains control over the collection, enabling easy sharing with external entities already connected to the model.
Embedding a Collection in Attributes
Achieving the desired outcome involves minor modifications, as outlined below.
Avoid Direct Object Embedding in defaults
Utilize a function that returns an object instead:
var Daymodel = Backbone.Model.extend({
defaults: function() {
return {
day: 1,
agenda: new Todocollection()
};
},
});
This prevents the shared placement of the agenda
collection across all instances of Daymodel
, ensuring individual creation per instance.
Maintain Collection Consistency
var Daymodel = Backbone.Model.extend({
defaults: { day: 1, },
initialize: function(attrs, options) {
var agenda = this.getAgenda();
if (!(agenda instanceof Todocollection)) {
return this.set('agenda', new Todocollection(agenda), { silent: true });
}
},
/**
* Parse function guarantees collection consistency.
*/
parse: function(response) {
if (_.has(response, 'agenda')) {
response.agenda = new Todocollection(response.agenda);
}
return response;
},
getAgenda: function() {
return this.get('agenda');
},
setAgenda: function(models, options) {
return this.getAgenda().set(models, options);
},
});
Enable Serialization
var Daymodel = Backbone.Model.extend({
/* ...snip... */
toJSON: function(options) {
var attrs = Daymodel.__super__.toJSON.apply(this, arguments),
agenda = attrs.agenda;
if (agenda) {
attrs.agenda = agenda.toJSON(options);
}
return attrs;
},
});
This principle applies seamlessly when integrating the collection into a model property.
Prevent Unintentional Attribute Overrides
Careful handling is required here to avoid conflicts as dataset complexity grows. While customizing the save
and set
functions can add verification checks, maintaining simplicity may outweigh potential gains in the long term.
Challenges Associated with Collections in Models
By advocating against direct inclusion in a model and suggesting a more methodical approach, performance concerns arising from multiple instances and nested collections within models can be mitigated. Delayed creation ensures resource usage remains optimal, activating specific models solely when needed.
Ready-to-Use Solutions
If implementing the above approaches seems daunting, comprehensive solutions are available: