Why are simple things so hard?


#1

Popular = Simple for simple things. Easy for hard things.

Building a simple App, all I want is get some JSON object from the server, mark it up and provide navigation to some other objects, no fancy binding just plain old JSON with markup and simple navigation.

So I read my model as:

App.ProjectsRoute = Ember.Route.extend({
    model: function () {
        return Ember.$.getJSON("api/project");
    },
});

Where the JSON looks like this:

{ "header":"This is the header",
  "Users": [
       { "Id": 1, "User": "Tom", "LastName": "Dale" }
      ,{ "Id": 2, "User": "Jason", "LastName": "Mitchell" }
  ] }

This model (JSON) object has some properties on it and some nested arrays. One would think that a simple #each in handlebars would do the trick in the template but - heck nada - no nothing.

Like this: (abbreviated, not Users is array of objects, all objects have a User attribute)

<h2 >Project {{ProjectAttachment}}</h2>
<p><dl class="dl-horizontal">
    <dt>Description:</dt><dd>{{header}}</dd>
   </dl>
{{Users}}
{{#each Users}} {{User}} {{/each}}<br />

So - why is this so hard - is it really necessary to have a whole Ember Data model defined with all the PITA associated with it to just ‘show a nested object’?

I gotta be on the wrong path here.


#2

Hard to say what you’re doing wrong without a link to your project.

But here is what you’re describing and working: http://jsfiddle.net/7ZWZg/10/


#3

jasonmit - that is what I thought would work too. After being puzzled I modified the provided Fiddle (see http://jsfiddle.net/7ZWZg/15/) with the data structures that are passed. Turns out that the case-sensitivity of Ember blows things up.

Nasty. Might need to inject a ‘lower case’ filter or something.


#4

In your case, you want to return a header and an array of users from your API, but Ember expects your model to be your array of users, not the whole response (header and all), so that’s why its getting confused. Easiest way to get it working like that is to call model.Users, like this.

http://jsfiddle.net/s7HWr/8/

Better yet is to stick a .then on the response and return the user array from there. Or you could manage it all in the setupController hook, or format the response like @jasonmit showed.


#5

ulisesrmzroche, jasonmit Thanks for your replies. However - the part that started the post and that is puzzling to me is the fact that I cannot take some JSON object (whether the properties are uppercase or lower case) and use a Handlebar template to display it.

Note the strucuture I have is { item1: “some text”, Users: [ { User: “name1” }, { User: “name2” } }

Why do I have to do something complicated (in a non typed language) to show some (untyped) data? Isn’t that the appeal of JS that I can ‘just do things’ and NOT have to defined and declare different ‘types/structures’ ??

Still puzzled? I must be missing something about Ember? Please enlighten me.


#6

Nice - Like it !! Very helpful!


#7

Because your payload is a little more complicated than Ember expects, you’ll have to specify the details. Ember is expecting your model to be an array of users, but doesn’t know what to do with the extra property. I handled it straight on the model route, but if you use Ember Data, you can use its Adapters to make it much cleaner and customizable.

http://emberjs.com/api/data/classes/DS.Adapter.html


#8

I got it to work by changing Users to users (downcasing) in both the xhr response and the template. Maybe this is a bug in canary. I think that ember is looking for Users in the global namespace because it starts with an uppercase letter.


#9

This is relevant: https://github.com/emberjs/ember.js/pull/3218


#10

This is indeed some quirkiness due to Ember wanting to treat Caps as a global. @oss it is indeed a bug, and the best work-around for now is to lowercase properties in your JSON response before passing them to part of Ember.


#11

Thanks for the explanation - that makes a lot of sense. The workaround provided by ‘ulisesrmzroche’ with using model.Users as a property reference for the {{#each}} loop helped me to get it moving. But it sure is counter intuitive and looks like a bug, but then I am using Ember the first time for a quick and dirty project where I figured it be worth exploring the capabilities.

Is there a release data/version for the fix?

BTW. Changing the model is not an option, the entire point is to use an existing API to provide a simple display of the nested data that appeals and provides navigation and the ability to link to other APIs. If I had to change the API (e.g. JSON response) it would undermine the purpose of quick and clean.

Thanks for all the guidance.


#12

The uppercase ‘Users’ is a red herring. See this JSBin, http://jsfiddle.net/s7HWr/9/, it’s the exact same result. Also, a lot of the time you may not have control over the API, whether for legacy reasons or because you don’t own it, so it’s not a repeatable practice. It shouldn’t matter what your API payload looks like though, it’s Ember’s job to adapt to it.

The real issue is that ember doesn’t know what to do with that extra header property unless you tell it what to do with it. Right now, it’s just iterating through the whole response instead of just the array of users. you can do this either via an adapter, manipulating the response inside the subsequent promise, etc.You just have to make sure that your model (not necessarily your whole payload) returns an array of users.

In practice, your template should just look like this though.

{{#each}}
  {{this}}
{{/each}}

#13

May wanna check Jeff’s Screencast too http://www.sparkcasts.net/posts/1-custom-adapters-for-ember-data


#14

Is where this assumption that capitalized words do not have a context is made.


#15

Globals in Ember aren’t simply capitalized, it uses paths, plus, it has to be namespaced to your app, so it’s not gonna get confused there.


#16

@oss, As I think @ulisesrmzroche is suggesting, using a custom Ember Data adapter/serializer to normalize the values from your API will be the best in the long term. If you want to just get something simple working, it’s pretty trivial to do something like this:

App.ProjectsRoute = Ember.Route.extend({
  model: function () {
    return Ember.$.getJSON("api/project").then(function(result) {
      result.users = result.Users;
      return result;
    };
  },
});

You are totally right in the case of ember-data, but I think @oss is looking for a simpler case. I was thinking the same thing when I first read the original question. Having spent a lot of time with custom adapters and serializers, it’s easy to forget that it is perfectly valid to just pass simple json objects directly from your api to your view (although this has the problem of being less maintainable).


#17

Yeah, pretty much the first one, except for that he also wants to display the Header in the template, so you have to return that too. The .then() doesn’t exist because he’s using httpRespond to seed it. That’s why it’s being backed by ObjectController in the jsbin and not ArrayController.