Handlebars nested objects - backbone.js

I am using backbone.js with nested models. The idea being self-contain all attributes of the author as well as to re-use the author model on both posts and comments. At least in theory this makes sense I would think.
However, setting things up like this I've run into a confusion how to retrieve the different values with Handlebars. Handlebars doesn't like being passed objects from what I've read. I can easily retrieve the status in a {{#each}} with {{status}} but naturally doing {{author.name}} won't work.
I've looked at using a Helper, however as you can see I have comments nested inside, that will have another author nested inside. I don't believe helpers inside helpers will work.
This is a sample object pulled from the console of Chrome.
Object {items: Array[2]}
+items: Array[2]
+0: Object
+author: child
_changing: false
_pending: false
_previousAttributes: Object
+attributes: Object
name: "Amy Pond"
profileImage: "Amy.jpg"
__proto__: Object
changed: Object
cid: "c0"
__proto__: Surrogate
comments: child
id: "50f5f5d4014e045f000001"
status: "1- This is a sample message."
__proto__: Object
+1: Object
author: child
comments: child
id: "50f5f5d4014e045f000002"
status: "2- This is another sample message."
__proto__: Object
length: 2
__proto__: Array[0]
__proto__: Object
Am I incorrect in my organization, or is there a better way to handle multidimensional data? Or is there a nice way for Handlebars to get to each of the values?
If there is a more powerful templating engine, I'm open options.

Seems the problem is that you put the Backbone model directly into your template, but you have to convert it into a JSON object first using model.toJSON(). Or you try to access author.attributes.name.
From the docs:
Handlebars also supports nested paths, making it possible to look up
properties nested below the current context.
<div class="entry">
<h1>{{title}}</h1>
<h2>By {{author.name}}</h2>
<div class="body">
{{body}}
</div>
</div>
That template works with this context
var context = {
title: "My First Blog Post!",
author: {
id: 47,
name: "Yehuda Katz"
},
body: "My first post. Wheeeee!"
};

Related

Access $scope object with protractor

I got an object like:
$scope.project = {name: 'whatever', description: 'blabla', another: 'another'};
To debug this, I enter in repl mode and try to see what "project" has.
When I define project variable as below, and call it, it returns my object, but when I try to access its keys (project.name), I get undefined. If I do Object.keys(project) I am getting the page object methods like click, getAttribute, etc.
Any ideas on how can I have access to the original object keys?
View side:
<h1 id="foo">{{project.name}}</h1>
Test side:
var project = element(by.id('foo')).evaluate('project');
evaluate uses executeScript behind the scenes. It returns an ElementFinder which resolves to the object you are looking for:
var project;
element(by.id('foo')).evaluate('project').then(function(value) {
project = value;
});
The documentation says:
which resolves to the evaluated expression for each underlying
element. The result will be resolved as in
webdriver.WebDriver.executeScript. In summary - primitives will be
resolved as is, functions will be converted to string, and elements
will be returned as a WebElement.
Also, check out Accessing Angular inside Protractor Test
Edit: syntax error

Is there anything similar to "KO.mapping.fromJS" in AngularJS?

I wanted to attach some new calculated property to a complex json object returned from a REST Service. This can be easily achieved through KnockoutJS's Mapping pluggin.
But I have decided to go for AngularJS this time. Is there any modules/pluggins similar to knockout's mapping pluggin ??
my PROBLEM is as shown below:
JSON Returned from server is something like:
{
id:2,
name: 'jhon',
relatives:[
{id:1,name:'linda', score:{A:10,B:33,C:78} },
{id:2,name:'joseph', score:{A:20,B:53,C:68} },
{id:3, name:'keith', score:{A:40,B:83,C:30} }
]
}
in the above json object, I want to attach some calculated property to each objects inside "relatives" collection based on the score each relative has.
Try using
angular.extend($scope, data);
I'm also starting to use Angular, coming from Knockout and Durandal :) Hope this might work for you. The data should be accessible in your view ($scope) directly.
Edit: See this thread.
Basically in angular there is no thing similar to Observable variables.
AngularJS makes observing separately from $scope itself.
To make map from Json in AngularJS you can use angular.fromJson to bind data from Json.
To add fields to your scope you also can use angular.extend.
But anyway adding calculated field is thing that you need to make by yourself, for this purpose you can try to use watch methods: $scope.watch, $scope.watchGroup,
watchCollection.

Backbone.js How to shuffle items within a Collection

I'm having issues when trying to randomize the order of objects in a collection.
Here's the code that I've tried:
console.log(this.collection);
shuffled = this.collection.shuffle();
console.log(shuffled);
And here's the console output (using a test collection with 3 items):
child {models: Array[3], length: 3, _byId: Object, url: "/myurl/myid", _listenerId: "l7"…}
_byId: Object
_events: Object
_idAttr: "id"
_listenerId: "l7"
length: 3
models: Array[3]
__proto__: Surrogate
[child, child, child]
0: child
1: child
2: child
length: 3
__proto__: Array[0]
As you can see, the Collection is not being shuffled properly, instead it's creating a new unuseable object full of pesky children.
All I'm trying to do is randomize the order in which the models appear in the collection before passing it to a view for display (I'm creating a button called "randomize" which needs to randomize the display of the items in the collection). I thought this would be an easy task, but at this point I'm considering just creating a whole new model and doing the shuffle on the server.
Any help is greatly appreciated!
console.log(this.collection);
this.collection.reset(this.collection.shuffle(), {silent:true});
console.log(this.collection);

Backbone / Underscore chain method with where method

There has to be something simple I am missing here.
http://jsfiddle.net/v9mdZ/
I am just learning Backbone and Underscore/loDash and am trying to get familiar with chain.
I have the following code, which works as expected:
var ids = _.pluck(collection.where({'is_checked':true}), 'id');
I attempted to refactor this, using chain like so:
var ids = collection.chain().where({'is_checked':true}).pluck('id').value();
Why doesn't the refactored code work? Am I using chain wrong?
Solution (details below)
Don't use where with chain.
The merging of some Underscore methods into collections is a little imperfect. When you say collection.some_mixed_in_underscore_method(), the collection unwraps some of the Backbone stuff behind your back so that the Underscore method is applied to the attributes inside the collection's models; it sort of works like this:
var ary = _(this.models).map(function(m) { return m.attributes });
return _(ary).some_mixed_in_underscore_method();
But collection.chain() doesn't work like that, chain just wraps the collection's models directly so if you do this:
console.log(collection.chain());
you'll see that chain is giving you an object that wraps an array of models. Your models won't have an is_checked property (i.e. there is no model.is_checked), they will have is_checked attributes though (i.e. there will be model.get('is_checked') and model.attributes.is_checked).
Now we can see where everything goes wrong:
collection.chain().where({'is_checked':true})
The models don't have is_checked properties. In particular, there won't be any models where is_checked is true and everything after the where is working with an empty array.
Now that we know where things go sideways, how do we fix it? Well, you could use filter instead of where so that you can easily unpack the models:
collection.chain()
.filter(function(m) { return m.get('is_checked') })
.pluck('id')
.value();
But, your models don't have ids yet as you didn't create them with ids and you haven't talked to a server to get ids so you're going to get an array of undefineds back. If you add some ids:
var collection = new App.OptionCollection([
{id: 1, 'is_checked': true},
{id: 2, 'is_checked': true},
{id: 3, 'is_checked': false}
]);
then you'll get the [1,2] that you're looking for.
Demo: http://jsfiddle.net/ambiguous/kRmaD/

Access Nested Backbone Model Attributes from Mustache Template

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!
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.
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(); });
}
});
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.
The way to go about the same in Mustache would be as follows:
Hi
{{FirstName}}, you live in {{#Address}}{{City}} {{/Address}}
Hope it helps..

Resources