Reformatting model data in controller

I have a new app I am trying to build using Ember. Got a Rails backend that Ember access via REST. The REST API returns data that isn’t in the format I need it in for the template (specifically for the google map that is embedded).

The page I am working on shows a list of locations on one side, and then I am trying to show markers for those locations on a google map on the right side. I have working code to take the data I currently get and do the text display (using HTMLBars) but that format isn’t correct for adding as markers to a google map.

Seems like I should be able to use the controller to reformat the data to support the specific display needs of the template, correct? I know that in Ember you don’t use the controller to make the ajax calls, but I thought it was supposed to support the template itself (and formatting seems to be part of that).

What’s the right way to access the model data from the Route inside the Controller class “Ember.Controller.extend({…” so that I can fix the formatting.

Currently I am trying this, and it doesn’t appear to be executed.

Because I can’t figure out how to paste multi-line JS into this and have it formatted, I put it into a GIST here. https://gist.github.com/anonymous/600490be59032ad9b7e7

Ember : 1.13.7 Ember Data : 1.13.8 jQuery : 1.11.3

Just as a reference of what versions.

You can change the data from a route in a component like this:

https://github.com/broerse/ember-cli-blog/blob/master/app/components/blog-posts.js

You can set the models with controller.setProperties(models); like this:

https://github.com/broerse/ember-cli-blog/blob/master/app/routes/posts.js

You sent the post model to the component like this:

ember-cli-blog/posts.hbs at master · broerse/ember-cli-blog · GitHub

Ember 1.13.8 is giving me less problems…

So how do I reformat the data when I don’t want to use a component.

Specifically I am trying to use the ember-google-maps addon to display a google map (with markers) on my page. The model data I get back in the route contains all the data points, but not formatted correctly to hand to the ember-google-maps add on. I have tried extending their MarkerController to let that try to fix the data for each individual marker, but that doesn’t seem to work (and produces no errors) I suspect because the resulting API has a single top level element “data” that then contains the array of locations I would want to map.

What I am looking to do it take the JSON i get back from the API, extract the array of location objects from that, and then put that into a new object with the correct key names to satisfy the ember-google-maps add on.

There has to be clean ways to massage the data that gets returned before it’s presented to the templates (or components) is there?

You should not use array controllers for this but you still can. See:

https://github.com/broerse/ember-cli-blog/commit/2f9af92d9b2728d8f49a6868343cbdefbc606310

But the data being returned (at the top level) isn’t an array. And it’s all from a single REST end point that I am calling.

But lets make this simpler. When I pass the locations to the google maps to have it generate markers, it expects the JSON object to be

 { title: "Title", lat: 123, lng: -98}

But when the data comes back from my API its

{ data: [
 { id: 1, latitude: 34.5, longitude: -95.34, .....},
 { id: 2, latitude: 56.7, longitude: -54.3, .....}
 ]}

part of my page can work with the model object as returned without any issue (and it actually works better), but to pass it to the ember-google-maps part I need to convert the JSON returned (bottom example) into the format at the top.

I dont want to issue a second API call. I don’t have an array being returned from the API and I don’t have mulitple data sources, so the ArrayController is out.

Where, in Ember, is the appropriate place to do transformations of data into the format that various templates/views/components need? I would have thought it was the controller, but maybe I am looking in the wrong place.

You can still reformat the data on the controller. Set the data you get from your API on the route on the controller like in the second link above like this:

  model: function() {
    return Ember.RSVP.hash({
      content: get_your_data()
    });
  },

  setupController: function(controller, models) {
    controller.setProperties(models);
  },
1 Like

It’s really not intuitive (and the documentation doesn’t provide any help to clarify this) that Ember can figure out which controller to load via convention (naming) but it doesn’t automatically make the model available to that controller without explicitly setting it.

Is this the only way to get access to the model in the controller?

If so, then someone at a minimum needs to make this much more clear in the documentation.

If you do not override setupController, the model is made available. You can call _super in setupController to ensure the default behavior still occurs.

I am not sure I understand what you are saying here.

If I don’t call setupController in the routes, how do I get access to the model in the controller? Note that I am not currently calling the setupController method at all and allowing the naming scheme convention to connect the correct controller without me explicitly defining it.

Can you provide an example of how to access the model in the controller without calling setupController in the routes?

I didn’t say don’t call setupController, I said don’t override it. It has a default implementation which sets the model on the controller.

For an example, see: Ember Twiddle

In index/route.js setupController has not been overridden.

In index/controller.js, ‘model’ is available.

@Gaurav0

Thanks, that actually helps. Any chance you can share a bigger example that shows people actually manipulating the contents of the model in the controller? I have tried various options, but still could never seem to figure out how to reference the model the right way.

I see that you used Ember.computed.readOnly to access the text property of the model object. What’s the equivalent to iterate over a collection of objects returned in the model (like a list of model objects).

I am sorry to keep asking these questions, but I can’t find any comprehensive answers to these types of questions. That makes me think either I am not following the “ember way” on this, or the documentation just has big holes. Hard for a newbie to know the difference.

Sorry if I missed something previously in this thread. Are you trying to access the model from the controller? How about this.get('model')?

Since templates have 1:1 relationships with controllers (aside from components), iterating a collection-model would look like:

{{#each model as |item|}}
  {{ item.text }}
{{/each}}
1 Like

Here is a really complex example of manipulating data in a model from the Ember Twiddle source code. It is done in a component, but the code would be the same in a controller.

So I am trying to follow this, but it doesn’t seem to work.

let stations = this.get('model); // objects are mapped into models via serializer and associated station.js class
console.log(stations) // Output tells me that it's an array

But when done this way, I can’t iterate over it to do the reformatting. As various different options of iteration (Ember.forEach, tradtional for loops based on stations.length, etc…)

I am sure that I am just missing some small piece of the puzzle here, but I can’t find any good documentation on how to do this.

I know how to iterate over the model in the template, the problem seems to be figuring out how to do it in the controller itself. All of my obvious attempts (see later responses) aren’t working.

If I call this.get(‘model’); It gives me back an object, but I can’t figure out how to do anything with it from there.

It takes some time to sink in. Perhaps take a look the at {{#each pagedContent as |post|}}:

https://github.com/broerse/ember-cli-blog/blob/master/app/templates/components/blog-posts.hbs

You can run my example without installing CouchDB:

git clone https://github.com/broerse/ember-cli-blog.git

I hope it will help you.

I know how to iterate over the returned array of objects in the templates. But I can’t seem to reference those objects the same way in the controller (which is where I have to do this).

I looked through you code example and it doesn’t show any places where you are iterating over a array in the model inside the controller.

jesmith, I promise that what you want to do is easy – I’m just kind of lost in translation here.

From a high-level view, things would go along the lines of:

Rails API → Ember Data Adapter/Serializer → Route (model, afterModel, setupController) → controller/template

So you the data you are receiving from the API passes through the serializer. You have the ability to massage data there. But you mention that you have your Station models alright. The second option is, once you receive data in model, to massage/transform it using the afterModel hook.

model() {
   return this.store.findAll('station');
},

afterModel(model) {
  return transform(model);
}

Whatever you returned in model/afterModel will be available in the controller/template as a model property. Also, please show us what this.get('model') returns, because if it’s an array you should be able to loop it with forEach.

(I feel your frustration, which I’ll answer later on the other thread. Hold on, it will click, I promise)

1 Like

That is because we now do this in a component like this.

pagedContent, filteredContent and arrangedContent are arrays. You can change and iterate over them there.

broerse, that’s a very complete example! I like how you structured your blog sample app.

Perhaps I’m mistaken, but I’m under the impression that he is very new to Ember and wants to achieve something quite… simple actually. While you are here helping and your example does cover a lot – couldn’t that be causing confusion?

ArrayProxies, imports, mixins, components (does he know that a component acts like a controller?)… all these concepts are at least intermediate level and it could possibly overwhelm someone new to Ember. Just a thought.

1 Like