Brendan Delumpa
Brendan Delumpa

Reputation: 1145

Working with namespaced payloads in Backbone.js

I'm working on a project where the request and response payloads are all namespaced. For example:

{
    'SourceMachine':{
        'Host':'some value',
        'Description':'some value',
        'UserName':'some value',
        'Password':'some value'
    }
}

To be able to do get() and set() on the individual fields, I overrode the parse method in my "source_model" like so:

parse : function(response, xhr) {
    return response.SourceMachine;
}

That's all well and good. But the issue is this: When I want to do a POST or PUT operation, I have to get the Host, Description, UserName and Password attributes into the SourceMachine namespace. Basically what I've been doing is copying the model attributes to a temporary object, clearing out the model, then saving as follows:

var tempAttributes = this.model.attributes;
this.model.clear();
this.model.save({SourceMachine: tempAttributes});

This works, but it screams of KLUGE! There's got to be a better way of working with namespaced data. Thanks!

Update

I've now created a require.js module for a base model that I'll be using for all the models in my app, since they all rely on namespaced data:

define(function() {
    return Backbone.Model.extend({
        /**
         * Adding namespace checking at the constructor level as opposed to
         * initialize so that subclasses needn't invoke the upstream initialize.
         * @param attributes
         * @param options
         */
        constructor : function(attributes, options) {
            //Need to account for when a model is instantiated with
            //no attributes. In this case, we have to take namespace from
            //attributes.
            this._namespace = options.namespace || attributes.namespace;

            //invoke the default constructor so the model goes through
            //its normal Backbone setup and initialize() is invoked as normal.
            Backbone.Model.apply(this, arguments);
        },
        /**
         * This parse override checks to see if a namespace was provided, and if so,
         * it will strip it out and return response[this._namespace
         * @param response
         * @param xhr
         * @return {*}
         */
        parse : function(response, xhr) {
            //If a namespace is defined you have to make sure that
            //it exists in the response; otherwise, an error will be
            //thrown.
            return (this._namespace && response[this._namespace]) ? response[this._namespace] 
                                                                  : response;
        },
        /**
         * In overriding toJSON, we check to see if a namespace was defined. If so,
         * then create a namespace node and assign the attributes to it. Otherwise,
         * just call the "super" toJSON.
         * @return {Object}
         */
        toJSON : function() {
            var respObj = {};
            var attr = Backbone.Model.prototype.toJSON.apply(this);
            if (this._namespace) {
                respObj[this._namespace] = attr;
            } else {
                respObj = attr;
            }
            return respObj;
        }
    })
});

Upvotes: 2

Views: 418

Answers (1)

mu is too short
mu is too short

Reputation: 434785

Create and update operations on models call toJSON to produce the data for the server:

toJSON model.toJSON()

Return a copy of the model's attributes for JSON stringification. This can be used for persistence, serialization, or for augmentation before being handed off to a view.

You could provide your own toJSON:

toJSON: function() {
    return { SourceMachine: _(this.attributes).clone() };
}

That should make your server happy. Unfortunately, toJSON is also commonly used to provide data for your templates and you probably don't want the SourceMachine noise in there; if so, then add another method to prepare data for your templates:

// Or whatever you want to call it...
valuesForTemplate: function() {
    return _(this.attributes).clone();
}

That's what the standard toJSON method does internally.

Upvotes: 2

Related Questions