I'm slowly starting to get the hang of EmberJS. Unfortunately I've run into an issue I can't seem to work out.
I have a plex data-structure, which I retrieve through JSON, with multiple nested arrays and I can't seem to nest #each helpers.
I've setup my template as follows (shortened):
{{#each Servers}}
<div class="server">
<h1>{{unbound Name}}</h1>
Time: {{jsonDate CurrentTime}}<br />
<table>
{{#each Processes}}
<tr>
<td>{{unbound Name}}</td>
<td>{{unbound Location}}</td>
</tr>
{{/each}}
</table>
</div>
{{#/each}}
The second loop doesn't seem to run, when I modify Ember to log a message, #the second each is called, but it seems it doesn't know what to do.
When I replace the second #each with #Queue, it works, but right before the -element an "undefined"-text is inserted for every element in the list (minus 1).
When I move the #each outside of the other loop and put in the direct path to Queue (eg. Servers.0.Queue) it works fine, so it's certainly not the data.
How do I fix this? If nested #each is not possible, how do I prefend the "undefined"-text for the other method? Any other possibilities?
PS. I use unbound for performance reasons, I update the Servers object in one go and observe that, so there is no need to use bound properties - as I've noticed it significantly reduces browser performance (13% CPU Usage, whereas unbound gave me 0%). Not sure if related.
EDIT
Please see: /
The ServerOverview2a-method works after all, apparently ServerOverview2b generates the "undefined"-text I described earlier - probably should file a bug report for that?
My question now is: Why do nested #each not work whereas #Processes does?
I'm slowly starting to get the hang of EmberJS. Unfortunately I've run into an issue I can't seem to work out.
I have a plex data-structure, which I retrieve through JSON, with multiple nested arrays and I can't seem to nest #each helpers.
I've setup my template as follows (shortened):
{{#each Servers}}
<div class="server">
<h1>{{unbound Name}}</h1>
Time: {{jsonDate CurrentTime}}<br />
<table>
{{#each Processes}}
<tr>
<td>{{unbound Name}}</td>
<td>{{unbound Location}}</td>
</tr>
{{/each}}
</table>
</div>
{{#/each}}
The second loop doesn't seem to run, when I modify Ember to log a message, #the second each is called, but it seems it doesn't know what to do.
When I replace the second #each with #Queue, it works, but right before the -element an "undefined"-text is inserted for every element in the list (minus 1).
When I move the #each outside of the other loop and put in the direct path to Queue (eg. Servers.0.Queue) it works fine, so it's certainly not the data.
How do I fix this? If nested #each is not possible, how do I prefend the "undefined"-text for the other method? Any other possibilities?
PS. I use unbound for performance reasons, I update the Servers object in one go and observe that, so there is no need to use bound properties - as I've noticed it significantly reduces browser performance (13% CPU Usage, whereas unbound gave me 0%). Not sure if related.
EDIT
Please see: http://jsfiddle/PTC9B/7/
The ServerOverview2a-method works after all, apparently ServerOverview2b generates the "undefined"-text I described earlier - probably should file a bug report for that?
My question now is: Why do nested #each not work whereas #Processes does?
Share Improve this question edited Apr 24, 2012 at 22:14 Lennard Fonteijn asked Apr 24, 2012 at 21:12 Lennard FonteijnLennard Fonteijn 2,6512 gold badges26 silver badges39 bronze badges 2- Can you provide a JSFiddle, especially with your JSON structure? Take this as a starting point: jsfiddle/pangratz666/PTC9B – pangratz Commented Apr 24, 2012 at 21:20
- Made all my described cases and actually found a repro for the "undefined"-bug (ServerOverview2b): jsfiddle/PTC9B/7 I'll update the initial question as apparantly the second method works after all, I"m now wondering, why doesn't #each? – Lennard Fonteijn Commented Apr 24, 2012 at 22:10
3 Answers
Reset to default 6It looks like the properties in your hash are causing the troubles: using an Uppercase property Processes
doesn't work - if you change it to processes
the each
helper works as expected, see http://jsfiddle/pangratz666/ndkWt/:
<script type="text/x-handlebars" data-template-name="app-server">
<h1>Default</h1>
{{#each data.Servers}}
<div class="server">
<h1>{{unbound this.Name}}</h1>
Time: {{unbound this.CurrentTime}}<br />
<table>
{{#each this.processes}}
<tr>
<td>{{unbound Name}}</td>
<td>{{unbound Location}}</td>
</tr>
{{/each}}
</table>
</div>
{{/each}}
</script>
App = Ember.Application.create({
ready: function() {
Ember.View.create({
templateName: 'app-server',
dataBinding: 'App.dataController.data'
}).append();
App.dataController.getServers();
}
});
App.dataController = Ember.Object.create({
data: {},
getServers: function() {
this.set('data', Ember.Object.create({
"Servers": [
{
"Name": "USWest",
"CurrentTime": "1337",
"processes": [
{
"Name": "apache.exe",
...
}
]}
]
}));
}
});
IMHO referring to this.Processes
should work in the #each
helper, so this might be a bug. Are you able to change the property names of the JSON which you get from the server? Otherwise you might write a helper which lowercases your property names of a JSON before it's used ...
Another note: the Application#ready
didn't work in your provided JSFiddle, because you specified the JS to be executed onDomReady
(select dropdown in upper left corner of JSFiddle). If you change this to no wrap
you can access App
within your ready
callback.
Another note about naming: instances should be named lowerCase and classes UpperCase. So it would be App.serverOverview1 = Ember.View.create({ ... });
in your example.
Ember attempts to infer whether your path is absolute (global) or relative (local) by whether or not it has uppercase properties. In this case, Ember is looking for the global Processes
property which doesn't exist. The simple solution is to use lowercase as @pangratz has pointed out. See http://www.emberist./2012/04/09/naming-conventions.html for more information.
Here is the post-processing method I made:
function decapitalizeJSON(data, dataType) {
var output = (dataType == undefined || dataType == '[object Object]') ? {} : [];
var value, type;
for(var key in data) {
if(!data.hasOwnProperty(key)) {
continue;
}
value = data[key];
type = Object.prototype.toString.apply(value);
if (type == '[object Array]' || type == '[object Object]') {
output[key.charAt(0).toLowerCase() + key.substr(1)] = decapitalizeJSON(value, type);
}
else {
output[key.charAt(0).toLowerCase() + key.substr(1)] = value;
}
}
return output;
}
It however is tediously slow - to no surprise. The has hasOwnProperty
is required to prevent it from trying to copy methods Ember adds to the Array object and it's recursive to be sure it decapitalizes everything. It can probably be optimized in my case to only decapitalize arrays, but then you're mixing naming conventions again.
Instead I just requested the API supplier to add in an "emberHack" property, which if passed provides me the proper JSON Ember expects and that has been granted - but said to be a "lame" requirement. I can guarantee you that most API suppliers wont be as lenient as mine though.