Access Nested Backbone Model Attributes from Mustache Template
Asked Answered
I

4

12

I have one Backbone model which has an attribute that is a reference to another Backbone model. For example, a Person has a reference to an Address object.

Person
  FirstName
  LastName
  Address
    Street
    City
    State
    Zip

These are classes that extend the Backbone model. So, then if I construct an object like the following...

var address = new Address({ Street: "123 Main", City: "Austin" });
var person = new Person({ FirstName: "John", Address: address });

I cannot seem to figure out how to access it in my Mustache template.

Hi {{FirstName}}, you live in {{Address.City}}.

Obviously does not work. When I look at the internals in Firebug, Address is an object, but the City is an attribute within the attributes object of Address. I cannot find any examples of how to access these attributes of associated objects.

I appreciate any help! Thanks!

Ivon answered 4/6, 2011 at 1:27 Comment(0)
E
5

Try using Handlebars, a templating engine based on Mustache with nested properties support.

Then it would be as easy as {{Address/City}}.

If you don't want to change your templating engine, you can flatten results from Address object and pass them as properties directly on the Person.

Empower answered 4/6, 2011 at 14:5 Comment(2)
Thanks, I am definitely going to try Handlebars. I am still curious about how Backbone's toJSON handles associated models. It appears (from looking in Firebug) that it nests attributes of the associated model under the "attributes" member. I need to test to see if Handlebars can find Address/City, or if I might need to do something like Address/attributes/City. Not sure. Probably a different question.Ivon
@Ivon Hey Kevin, did you ever figure this out: how to do Address/attributes/City properly?Bussard
I
8

I ended up solving this issue with the following approach.

I switched from Mustache.js to Handlebars.js for the templating engine. This allowed me to use path based expressions to access nested or associated objects and their attributes.

Hi {{FirstName}}. You live in {{Address.City}}.

But, I also had to change the way I was passing a JSON object to the template. I was using the toJSON method that is part of the Backbone.Model class. But, this was not generating JSON for the associated Address correctly (for the templating to work.) It was burying the address attributes in a member titled "attributes". So, instead, I ended up doing this...

var jsonForTemplate = JSON.parse(JSON.stringify(person));

This gave me a "raw" version of the objects and their associated objects which the template could access using the syntax shown above. JSON.parse and JSON.stringify are both part of json2.js.

Ivon answered 4/6, 2011 at 18:52 Comment(1)
Oh great, this is the solution I've been looking for a long timeBussard
H
6

I handled this by making another version of toJSON called deepToJSON that recursively traverses nested models and collections. The return value of that function can then be passed to a handlebars.js template.

Here is the code:

_.extend(Backbone.Model.prototype, {
  // Version of toJSON that traverses nested models
  deepToJSON: function() {
    var obj = this.toJSON();
    _.each(_.keys(obj), function(key) {
      if (_.isFunction(obj[key].deepToJSON)) {
        obj[key] = obj[key].deepToJSON();
      }
    });
    return obj;
  }
});

_.extend(Backbone.Collection.prototype, {
  // Version of toJSON that traverses nested models
  deepToJSON: function() {
    return this.map(function(model){ return model.deepToJSON(); });
  }
});
Herodotus answered 8/7, 2011 at 23:6 Comment(4)
Thanks. I wonder if this performs better than my approach above. I think the end result is similar.Ivon
I imagine performance is about the same. Code readability and the minimization of mistakes is the benefit here, imo.Aliped
Thanks for this. I had to modify yours slightly to check the object value for null before recursively calling deepToJSON: if (!(_.isNull(obj[key]) || _.isUndefined(obj[key])) && _.isFunction(obj[key].deepToJSON)) {Fang
@EricHu ha i just did the same thing (your comment was below the fold on my browser) and was about to repost the same exact comment.Hinduism
E
5

Try using Handlebars, a templating engine based on Mustache with nested properties support.

Then it would be as easy as {{Address/City}}.

If you don't want to change your templating engine, you can flatten results from Address object and pass them as properties directly on the Person.

Empower answered 4/6, 2011 at 14:5 Comment(2)
Thanks, I am definitely going to try Handlebars. I am still curious about how Backbone's toJSON handles associated models. It appears (from looking in Firebug) that it nests attributes of the associated model under the "attributes" member. I need to test to see if Handlebars can find Address/City, or if I might need to do something like Address/attributes/City. Not sure. Probably a different question.Ivon
@Ivon Hey Kevin, did you ever figure this out: how to do Address/attributes/City properly?Bussard
N
3

The way to go about the same in Mustache would be as follows:

Hi

{{FirstName}}, you live in {{#Address}}{{City}} {{/Address}}

Hope it helps..

Nickolas answered 27/7, 2011 at 15:12 Comment(2)
Doesn't work for me, at least when using pystache. Can't tell if it's a bug or intended behavior.Brakesman
If you passing backbone model ( "this.model.toJSON()" ) to template adding {{#attributes}} may help. for example. {{#attributes}} {{#Address}}{{City}} {{/Address}} {{/attributes}}Demona

© 2022 - 2024 — McMap. All rights reserved.