user4374103
user4374103

Reputation: 21

Creating a Backbone.js Model with complex JSON

I have JSON response as follows

{
    "results": [
        {
            "name": "FOO",
            "containerName": "Foo",
            "accounts": [
                {
                    "id": "10445570_7601",
                    "shareeAccountInfo": "",
                    "siteAccountId": "271555",
                    "siteId": "271555",
                    "refreshMode": "NORMAL",
                    "isNetIncl": "true",
                    "propertyId": null,
                    "amount": [
                        "0.0",
                        "USD"
                    ]
                },
                {
                    "id": "1070_20537601",
                    "shareeAccountInfo": "",
                    "siteAccountId": "271555",
                    "siteId": "271555",
                    "refreshMode": "NORMAL",
                    "isNetIncl": "true",
                    "propertyId": null,
                    "amount": [
                        "0.0",
                        "USD"
                    ]
                }
            ]
        },
        {
            "name": "FOO123",
            "containerName": "Foo123",
            "accounts": [
                {
                    "id": "10445570_20601",
                    "shareeAccountInfo": "",
                    "siteAccountId": "271555",
                    "siteId": "271555",
                    "refreshMode": "NORMAL",
                    "isNetIncl": "true",
                    "propertyId": null,
                    "amount": [
                        "0.0",
                        "USD"
                    ]
                },
                {
                    "id": "10445570_37601",
                    "shareeAccountInfo": "",
                    "siteAccountId": "271555",
                    "siteId": "271555",
                    "refreshMode": "NORMAL",
                    "isNetIncl": "true",
                    "propertyId": null,
                    "amount": [
                        "0.0",
                        "USD"
                    ]
                }
            ]
        },
        {
            "name": "FOO83838",
            "containerName": "Foo3232",
            "accounts": [
                {
                    "id": "1601",
                    "shareeAccountInfo": "",
                    "siteAccountId": "271555",
                    "siteId": "271555",
                    "refreshMode": "NORMAL",
                    "isNetIncl": "true",
                    "propertyId": null,
                    "amount": [
                        "0.0",
                        "USD"
                    ]
                }
            ]
        }
    ]
}

I am having issues creating a Backbone Model from this JSON response. Should I be using a nested Model? and how should I be creating a collection based of my Model? Instead will it be a good idea to flatten this JSON structure? any ideas?

Upvotes: 2

Views: 811

Answers (1)

joews
joews

Reputation: 30330

Your data structure naturally fits a Collection of Models (I'll call the model Group), where each Group contains a collection of Account models. This collection (and optionally its models) should have a reference to the parent Group.

var Account = Backbone.Model.extend({

})

var Accounts = Backbone.Collection.extend({
  model: Account, 
  initialize: function(models, options) {
    this.parent = options.parent;
  }
});

var Group = Backbone.Model.extend({
  initialize: function() {
    this.accounts = new Accounts([], { parent: this });
  }
});

var Groups = Backbone.Collection.extend({
  model: Group,

  // Assuming you make requests to `/group` to produce your result JSON
  url: 'group',

  // Construct models from the `results` attribute of the response
  parse: function(response) {
    return response.results;
  }
});

There are two main implementation choices to make:

Persistence

If individual Accounts can be persisted seperately from the parent container, perhaps using an endpoint like /group/FOO83838/account/1601, the Acccount model can use the default Backbone.Model.save. The Accounts collection should override url to reference the parent URL:

Accounts = Backbone.Collection.extend({
  // code from earlier

  url: function() {
    return this.parent.url() + '/account';
  }
});

If accounts can only be saved as part of the overall Group model, you need to do two things:

First, override Account.save to delegate to the parent's save method:

Account = Backbone.Model.extend({
  // code from earlier

  save: function() {
    this.collection.parent.save();
  }
});

Second, override the Group.toJSON to include child accounts:

Group = Backbone.Model.extend({
  // code from earlier

  toJSON: function() {
    var json = Backbone.Model.prototype.toJSON.call(this);
    json.accounts = this.accounts.toJSON();
    return json;
  }
});

(In this example I have used the collection's parent reference. If you prefer you could also save a reference to the parent on this model).

Events

You could allow app code to directly listen to Group.accounts events, in which case no code changes are required:

// Example view code
this.listenTo(group.accounts, 'change', this.onAccountChange, this);

Or, if you prefer the extra encapsulation, you can forward child model changes:

Group = Backbone.Model.extend({
  // code from earlier

  initialize: function() {
    this.accounts = new Accounts([], { parent: this });
    this.listenTo(this.accounts, 'all', this.onChildEvent, this);
  }

  onChildEvent: function(eventName, model, options) {
    // write logic to whitelist the events and parameters you are interested in
    this.trigger('child:' + eventName, model, options); 
  }
});


// Example view code 
this.listenTo(group, 'child:change', this.onAccountChange, this);

You could also look into Backbone extensions like DeepModel (no longer maintained) or Relational. I usually prefer the finer control of a custom implementation.

Upvotes: 5

Related Questions