For every possibility where the new model is:
I found that hooking in the _prepareModel
undocumented collection function works well.
A generic collection
This collection can be used as-is to replace the default Backbone collection. It adds
- a new
onNewModel
fonction to override, that receives the new model instance and the options
- a custom
new-model
event which sends the same data.
var Collection = Backbone.Collection.extend({
/**
* Hook into the native _prepareModel to offer a standard hook
* when new models are added to the collection.
*/
_prepareModel: function(model, options) {
model = Collection.__super__._prepareModel.apply(this, arguments);
if (model) {
// call our new custom callback
this.onNewModel(model, options);
// trigger a new custom event
this.trigger('new-model', model, options);
}
return model;
},
// Called when adding a new model to the collection.
onNewModel: _.noop,
});
And your own collection could be:
var Menu = Collection.extend({
model: MenuItem,
initialize: function(models, options) {
this.name = options.name;
}
onNewModel: function(model, options) {
model.set({ menu: model.get('menu') || this.name });
},
});
It is guarantee that model
is a Backbone Model
instance inside onNewModel
.
Proof of concept
// The generic collection, put that in a file and include it once in your project.
var Collection = Backbone.Collection.extend({
/**
* Hook into the native _prepareModel to offer a standard hook
* when new models are added to the collection.
*/
_prepareModel: function(model, options) {
model = Collection.__super__._prepareModel.apply(this, arguments);
if (model) {
this.onNewModel(model, options);
this.trigger('new-model', model, options);
}
return model;
},
// Called when adding a new model to the collection.
onNewModel: _.noop,
});
// Extend from the generic collection to make your own.
var Menu = Collection.extend({
initialize: function(models, options) {
this.name = options.name;
},
onNewModel: function(model, options) {
model.set({
menu: model.get('menu') || this.name
});
console.log("onNewModel menu:", model.get('menu'));
},
});
// then use it
var menu = new Menu([
// works with bare objects
{
title: "Page",
url: "/page"
},
// or Model instances
new Backbone.Model({
title: "Other Page"
})
], {
name: "footer" // the collection option
});
// Listen to the custom event if you want
Backbone.listenTo(menu, 'new-model', function(model, options) {
console.log("'new-model' triggered with", model.get('title'));
});
// or other collection methods
menu.add({
title: "Page",
menu: "won't be overriden"
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone-min.js"></script>