Search code examples
javascriptbackbone.jsunderscore.js

Backbone - Array of Models to JSON


I know there's plenty of ways to do this... but why isn't this approach working?

var selected = this.collection.where({checked: true});

var mapped = _.map(selected, Backbone.Model.prototype.toJSON.call);

this.$el.html(this.template(mapped));

Error is Uncaught TypeError: undefined is not a function

Is there a more succinct way?

EDIT This did what I wanted...

var mapped = _.invoke(selected, Backbone.Model.prototype.toJSON);

EDIT 2 @nikoshr made it better.

var mapped = _.invoke(selected, 'toJSON');

Solution

  • Have a look in _.map source code, you will see, as of version 1.5.1

    _.map = _.collect = function(obj, iterator, context) {
        var results = [];
        if (obj == null) return results;
        if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
        each(obj, function(value, index, list) {
          results.push(iterator.call(context, value, index, list));
        });
        return results;
    };
    

    which means that for each object in selected, you will end with Backbone.Model.prototype.toJSON.call.call and an unspecified context, which probably isn't what you want.

    The simplest way to call toJSON on each model would be to use _.invoke:

    invoke _.invoke(list, methodName, [*arguments])
    Calls the method named by methodName on each value in the list.

    Applied to your case :

    var mapped = _.invoke(selected, 'toJSON');
    

    And a demo

    var c = new Backbone.Collection([
        {id: 1, checked: true},
        {id: 2, checked: false},
        {id: 3, checked: true}
    ]);
    var selected = c.where({checked: true});
    
    var mapped = _.invoke(selected, 'toJSON');
    console.log(mapped);
    <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>