Search code examples
jsonbackbone.jsnestedfetchbackbone.js-collections

Where are my Models after fetching a nested json backbone collection


Total Backbone js noob and I'm trying to understand whether the api I'm working with that returns json is either ill suited for backbone (or ill suited for anything), or if I just don't know how collections and models work enough yet in Backbone.

I know that the collection's fetch is returning the json correctly, it just may be how I'm processing the response. What is confusing is I don't really know what a proper fetched collection would like like and where the models are stored after fetching. It appears that what I want to be the models are stored as objects within the collection's models' attributes... is that right?

Also, I suppose as a second question, it seems that more or less my collection isn't parsing each model but rather only creates one model that contains my collection of models. I'm not sure how but I'd like that to be the other way around, one collection of my models. Any pointers?

Let me lay out some of the code and then I'll provide a sample of the json response (my sample will only include 3 objects), and a console log of the fetched collection. Really appreciate the help and any suggestions. Thanks!

My bootstrap file:

//statusApp.js 

(function($){

    var statusGroup = new app.StatusCollection();

    var deferred = statusGroup.fetch({
        data: {
            action: "get_my_status_collection"
        }
    });

    /* wait till fetch is complete */
    $.when(deferred).then(function(){

        console.log('returned collection', statusGroup);

        var statusGroupView = new app.allStatusView({ collection: statusGroup});

        $(".service-statuses").html(statusGroupView.render().el);

        var statusRouter = new app.Router();

        Backbone.history.start();
    });

})(jQuery);

and below is a sample of the response received showing the all the nested nodes it comes with from the server. You'll see in my collections' parse function only returns the "objects" node, and I don't need the rest (and it may not work with these top level nodes?)

{
  "component":{
    "status":"2",
    "count":"3",
    "objects":{
        "80":{
            "name":"Calendar",
            "parent":"0",
            "status":"3",
            "description":"Exchange Calendar",
            "comment":""
        },
        "220":{
            "name":"Solution Services",
            "parent":"0",
            "status":"3",
            "description":"",
            "comment":""
        },
        "2":{
            "name":"Tech Portal",
            "parent":"0",
            "status":"2",
            "description":"",
            "comment":""
        },
    }
  },
"in_maint":[],
"success":true
}

Here is a console log of the returned json from the collections fetch:

statusGroup 
d {length: 1, models: (...), _byId: Object, url: (...), trigger: function…}
    [object Object]: undefined
    get [object Object]: function () {
    set [object Object]: function (newval) {
    __backboneDebugger__appComponentInfo: window.__backboneAgent.AppComponentInfo
    __backboneDebugger__isInstancePatched: true
    _byId: Object
    initialize: function () {
    length: 1
    models: Array[1]
    0: d
        __backboneDebugger__appComponentInfo: window.__backboneAgent.AppComponentInfo
        __backboneDebugger__isInstancePatched: true
        _changing: false
        _events: Object
        _pending: false
        _previousAttributes: Object
        attributes: Object
            2: Object
                comment: ""
                description: ""
                name: "Tech Portal"
                parent: "0"
                status: "3"
            get 2: function () {
            set 2: function (newval) {
            80: Object
                comment: ""
                description: "Exchange Calendar"
                name: "Calendar"
                parent: "0"
                status: "2"
            get 80: function () {
            set 80: function (newval) {
            220: Object
                comment: ""
                description: ""
                name: "Solution Services"
                parent: "0"
                status: "3"
            get 220: function () {
            set 220: function (newval) {
            watchers: Object
            __proto__: Object
        get attributes: function () {
        set attributes: function (newval) {
        changed: Object
        cid: (...)
        get cid: function () {
        set cid: function (newval) {
        collection: (...)
        get collection: function () {
        set collection: function (newval) {
        id: (...)
        get id: function () {
        set id: function (newval) {
        initialize: (...)
        sync: function (){return b.sync.apply(this,arguments)}
        trigger: function (a){if(!this._events)return this;var b=g.call(arguments,1);if(!j(this,"trigger",a,b))return this;var c=this._events[a],d=this._events.all;return c&&k(c,b),d&&k(d,arguments),this}
        urlRoot: (...)
        get urlRoot: function () {
        set urlRoot: function (newval) {
        watchers: Object
        __proto__: f
    get 0: function () {
    set 0: function (newval) {
    length: 1
    pop: function () {
    push: function () {
    reverse: function () {
    shift: function () {
    slice: function () {
    sort: function () {
    unshift: function () {
    watchers: Object
    __proto__: Array[0]
get models: function () {
set models: function (newval) {
sync: function (){return b.sync.apply(this,arguments)}
trigger: function (a){if(!this._events)return this;var b=g.call(arguments,1);if(!j(this,"trigger",a,b))return this;var c=this._events[a],d=this._events.all;return c&&k(c,b),d&&k(d,arguments),this}
url: (...)
get url: function () {
set url: function (newval) {
watchers: Object
__proto__: f

My collection:

// allStatus.js

var app = app || {};

// A group (array) of Status models
app.StatusCollection = Backbone.Collection.extend({

    model: app.singleStatus,
    url: "/remote/api/status_json",
    parse: function(response){

        //return only the nested objects that will be our models
        return response.component.objects;

    }

 });

My Collection view:

// allStatusView.js

var $ = jQuery.noConflict();
var app = app || {};

app.allStatusView = Backbone.View.extend({

    tagName: "ul",

    render: function() {

        this.collection.each(this.addStatus, this);
        return this;
    },

    addStatus: function(status) {

        var statusView = new app.singleStatusView({ model: status });
        this.$el.append(statusView.render().el);
    }

});

My single view:

// singleStatusView.js

var $ = jQuery.noConflict();
var app = app || {};

app.singleStatusView = Backbone.View.extend({

    tagName: "li",
    className: "service-status",

    template: _.template( $("#statusElement").html() ),

    render: function() {

        var statusTemplate = this.template(this.model.toJSON());
        this.$el.html(statusTemplate);
        return this;
    }

});

Solution

  • Here's how you'd get the "objects" into the format your collection wants:

    app.StatusCollection = Backbone.Collection.extend({
    
        model: app.singleStatus,
        url: "/remote/api/status_json",
        parse: function (response){
    
            var obj = response.component.objects;
    
            // add the additional properties that won't be in your "models"
            // to the collection object directly if you want them
    
            this.status = response.component.status;
            this.count = response.component.count;
    
            // convert the "objects" object to an array and return
            // the resulting array
    
            return _.map(obj, function (value, key) {
              return obj[key];
            });
        }
    });
    

    I added the other properties from the "component" to the collection object directly, but this isn't necessary - they're only there if you needed to keep those as well. You could do the same thing with the other properties at the end that aren't part of "component".