Miguel Ehrstrand
Miguel Ehrstrand

Reputation: 71

Backbone.js How to make collection only accept one class of model

I am new to backbone.js and I am trying to learn it. In the code below I want my collection called "JokesCollection" to only accept adding models of the class "Joke". How do I do achieve this? When setting "Collection" attribute "model" to a certain model, isn´t the collection supposed to only accept that model class and ensure homogeneity? Don´t seam so. When I assign attribute "model" in the "JokesCollection" class to "Joke" it still accepts adding models of class "Persson" witch is not what I want. I only want it to accept adding models of class "Joke".

Joke = Backbone.Model.extend ({
    initialize: function(){
        console.log("Joke was created");
    },
    defaults: {
        joke : "",
        date : "0",
    }
});

JokesCollection = Backbone.Collection.extend({
    initialize: function(){
        console.log("JokesCollection was created");
    },
    model: Joke //  <=== Isn´t this supposed to ensure that the collection only accepts models of class "Joke"?
});

Person = Backbone.Model.extend ({
    initialize: function(){
        console.log("Person was created");
    },
    defaults: {
        username: "default",
        password: "default",
        email: "default"
    }
});



var person1 = new Person({username:"masterMind"});
var joke1 = new Joke({joke:"Girls are cute and funny hahahaha"});

jokesCollection = new JokesCollection();
jokesCollection.add(joke1);
jokesCollection.add(person1); // This adds a model of class "Person" to the collection. Witch is not what I want. It is not supposed to work! I want "jokesCollection" to only accept models of class "Joke".

console.log(jokesCollection.length); // length gets increased by 1 after adding "person1" to "jokesCollection". Again, it is no supposed to work from my point of view. I want "jokesCollection" to only accept models of class "Joke".
console.log(jokesCollection);

Upvotes: 0

Views: 84

Answers (2)

machineghost
machineghost

Reputation: 35813

The purpose of a Collection's model property is not to limit which models the Collection can accept. Rather, that property defines the Model class which the Collection will use when it needs to create a new Model. For instance,when you pass an object literal of Model attributes (as opposed to an instantiated Model) to JokesCollection.add, or when you fetch models in to a JokesCollection, Backbone will use Joke as the Model to instantiate those new additions to the Collection.

There are two ways to ensure your JokesCollection is only populated with instances of Joke. The first way is to never add Model instances to the JokesCollection directly, and instead either:

A) Bring new Jokes in from the server by calling fetch on a JokesCollection

B) add only "raw" Model attributes to the JokesCollection; don't add instantiated Models

However, if you're concerned about a developer accidentally adding a non-Joke Model to the Collection, your other option (as first suggested by @Evgeniy) is to overwrite your JokesCollection's add method. Unlike @Evgeniy's answer though I would not recommend re-writing Backbone's internals. Instead, I would use a simple overwrite that just calls the base Backbone method if possible:

add: function(models, options) {
    if (models instanceof Joke) {
        // Use the normal Backbone.Collection add method
        return Backbone.Collection.prototype.add.call(this, models, options);
    }
    var allModelsAreJokes = _(models).all(function(model) {
        return model instanceof Joke;
    ));
    if (allModelsAreJokes) {
        // Use the normal Backbone.Collection add method
        return Backbone.Collection.prototype.add.call(this, models, options);
    }
    // Handle the case where non-Jokes are passed in; either:
    // A) convert whatever was passed in to be a Joke:
    // var rawModels = _(models).isArray() ? _(models).invoke('toJSON') : model.toJSON();
    // return Backbone.Collection.prototype.add.call(this, rawModels, options);
    // B) just don't add anything
}

Upvotes: 1

Evgeniy
Evgeniy

Reputation: 2921

From official docs:

model collection.model

Override this property to specify the model class that the collection contains. If defined, you can pass raw attributes objects (and arrays) to add, create, and reset, and the attributes will be converted into a model of the proper type.

Looks like will have to re-write add method something like this :

add: function(models, options) {

    var modelClass = this.model;
        isProperIns = this.models.every.(function(model){
            return model instanceof modelClass;
        });

    if (!isProperIns) {
        throw new Error("Some of models has unacceptable type")
    } 

    return this.set(models, _.extend({merge: false}, options, addOptions));
}

Upvotes: 1

Related Questions