Accessing Model Data in Template - ember.js

I have a route defined like so:
Router.map(function() {
this.route('games', function() {
this.route('game', {path: '/:game_id'}, function() {});
});
});
When I visit path /games/1 I have to access the model data like so:
{{model.title}}
Reading the documentation, it seems that the controller would make the model data available without the model prefix so I thought I'd be able to do this:
{{title}}
But it doesn't work without model. I'm not creating the controller because I don't need to extend it for this particular route/template. Is accessing the model data via the model prefix accurate? Or am I possibly doing something else wrong?

The docs you are referring to are old, in the past (pre 1.13 iirc) the controller had a model proxy that would look up properties on the model if they weren't found on the controller.
This is no longer available since it was causing confusion in the community.
The new versions of the docs no longer reference {{title}} without model.
If you really want to use it without the model prefix you could resort to Ember.computed.alias though I would recommend sticking with model. as it is easier to read and identify as a model property vs controller property, especially since routable components are set to replace controllers in the near future (~2.6).

You can either use {{model.title}} or {{content.title}}. Earlier it possible to access properties directly using ObjectControllers but now as those are gone there is no other way.
[Edits]
There is one more way, you can extend setupController in route and set title on controller itself. That way you'll be able to directly use {{title}}.
setupController: function(controller, model) {
this._super(controller, model);
controller.set('title', model.get('title'));
}
Hope this helps.

In Ember, your model must be loaded by a route as described in the documentation here.
Assuming you are using ember-cli your route will look like :
export default Ember.Route.extend({
model: function() {
// Must return some data or promises
}
});
Model function can either return an array of data or a promises that will resolve to an array.
If you are using ember-data your function return will look like this :
return this.store.find('game', params.game_id);
You can also request directly to an API :
return Ember.$.getJSON('https://your-website.com/game/' + params.game_id);

Related

emberjs providing data for multiple components

I have an Ember 2.11 application template with a few component placeholders at the moment (menu, breadcrumbs, related items) and an outlet which displays the main content which works fine. Now that I'm feeling more comfortable with the basics, I'm ready to try getting the breadcrumbs working.
I read about services, but I don't see that it is the right solution for breadcrumbs because it doesn't need to be persistent, it is based off the route. Although it is based off the route, I don't want to use the route literally as I want to use nice titles, and when viewing a specific item, the route doesn't accurately reflect what the breadcrumbs should show.
Since the breadcrumbs is based off the model that is being used for the display, I feel that I should be able to construct a breadcrumb object and then pass that into the component from the application template. I suppose this was the purpose of the controller back in the day. My thought was in the route to construct a breadcrumb object/property and return it with the model like RSVP and then I could access both in the template to pass the appropriate object to the appropriate component. But that seems wrong as the route should return an Ember data object, promise or Javascript array.
My current line of thinking is along these lines.
template/application.hbs
{{bread-crumbs crumbs=model.breadcrumbs}}
{{outlet}}
route/category/show
export default Ember.Route.extend({
model(params) {
let recipe = this.get('store').query('recipe', { category: params.category_id});
let crumbs = [{name: 'Category', link: 'category'},
{name: recipe.category.title, link: 'category.show', target: recipe.category.id}];
return {recipe: recipe, breadcrumbs: crumbs};
}
});
I'm not sure if this is the right way to approach this or if this will cause problems with async data fetching with Ember data. Something like this I would have to define on each route, but I don't have a lot of routes and seems to offer flexibility when I'm displaying a recipe (route is /recipe/recipe_id), but have the breadcrumbs show Home > Categories > Main Dishes > My Awesome Dish.
How would you approach the problem?
Updated 2017-02-10:
It appears that the model is not passed to the application template, only the route template. I'm not sure how to pass data 'down' to the application template.
You could probably create a breadcrumb like this by tracking the elements in a service, but I'd check out the ember-crumbly addon. It seems like it will meet your needs.
Remaining in your thinking line, if you want to pass your model as a variable of your controller in the route that you are accessing you need something like this:
export default Ember.Route.extend({
model(params){
let recipe = ...;
let crumbs = ...;
return {...};
},
setupController(controller, model){
this._super(controller, model);
controller.set('variable', model);
}
});
Having this, in your controller you can access to the crumbs like this:
this.get('variable');
And in then with an Ember.computed you can create a variable for display in your template.
Sorry for the minimize your code but I'm not in my computer.
Another approach is setting the crumbs variable at the init of the application controller, this set the variables defined in that controller global to the application, so you can modify them from other controllers and in the application controller lookup for that changes via didUpdateAttrs() or with didUpadteElement().
Hope this resolve your problem.
Greetings

How do I sort models using only a route in Ember?

In the past you could use ArrayControllers (deprecated in 1.13.0), and we know that shortly controllers won't be recommended in ember. Is it currently possible to sort my model using only my route?
i.e
/routes/orders.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() { return this.store.findAll('order'); }
});
How would I sort by 'name' as an example, but only using a route?
Note that this question is similar to How to sort model in Ember.js? - but there it is via any method, whereas my question is specific to doing it only using a route (if possible).
This would seem to get the job done, unless I'm missing something.
export default Ember.Route.extend({
model: function() {
return this.store.findAll('order') .
then(orders => orders.sortBy('name'));
}
});
New answer post Ember 2.0
Although the question still stands and torazaburo's answer works great before Ember 2.0. The best answer now is "don't sort a model using only a route" - instead do the sorting in a controller, or if you don't want to use a controller then in a component.
There is a big 'gotcha' with the reload behaviour post Ember 2.0. If there are already records in the store, and you do not specify { reload: true } in the options of findAll, then the findAll method will instantly resolve with those records, meaning the then only sorts with those records you already had. So your model could return with a limited number of records while the actual background request is still going on. See DS Store docs for further info.
The improved code based on the previously accepted answer is therefore:
export default Ember.Route.extend({
model: function() {
return this.store.findAll('order', { reload: true }).
then(orders => orders.sortBy('name'));
}
});
But as previously mentioned, I think the best course of action is to not rely solely on the route, but instead use computed sort in either a controller or a component.

ember.js pulling from multiple model stores in a single template

i'm trying to learn how to create a form that draws from several models.
For example, a Person form that a user can specify their name, city, what Company(separate model) they belong to, what Groups(separate model) they're in, what Car(separate model) they drive, etc. I can't find any documentation on how this can be achieved.
In all the examples i've seen, the route is responsible for telling the template which singular model type to use. I have no idea how to create a dropdown or typeahead that pulls from a different model repository.
How can i accomplish this?
There are a couple of ways to accomplish this.
(1) Add a property to your controller that returns the necessary records for your drop down.
http://emberjs.jsbin.com/AqimiFI/4/edit
setupController: function(controller, model) {
this._super(controller, model);
// set an empty array
controller.set('states', []);
this.get('store').find('state').then(function(states){
//once the states are resolved set the states to the records
controller.set('states', states);
});
}
(2) At some point in your application (wherever it seems appropriate) in one of your routes create a controller for the items in question and set the model of that controller to the items, then use needs. I prefer this method, because you can use that controller all over the application add logic to it and have it be shared etc...
http://emberjs.jsbin.com/AqimiFI/5/edit
setupController: function(controller, model) {
this._super(controller, model);
var states = this.controllerFor('states');
states.set('model', this.get('store').find('state'));
}
App.ApplicationController = Ember.ArrayController.extend({
needs:['states'],
states:function(){
return this.get('controllers.states');
}.property('controllers.states')
});
In this example, I created a states controller in the application route. This isn't tying it to the application controller/route at all, it was just a hook early on that I could take advantage of for creating the controller to hold the data.
In order to access a controller from another controller you must specify that you need it (needs:['states']).
The states property is returning the states controller (it's important to remember that an array controller, and controllers in general, in ember are just decorators on their models). Ember will proxy all get/set calls down to the model (if they don't exist on the controller). So when I'm returning the states controller really you could think of it as just returning the model, which is the array of states.
So, you could try and set the property right on the controller, but it probably wouldn't work as expected. I'm taking advantage of the fact that I know if I set a promise on the model it will actually resolve that promise and replace the model with the result of that promise. It's just a little closer to the expected behavior of manually creating controllers.

How to link to nested resources in Ember.js?

Assume you have the following routes in an Ember application.
App.Router.map(function() {
this.resource('series', function() {
this.resource('serie', { path: '/:serie_id' }, function() {
this.resource('seasons', function() {
this.resource('season', { path: '/:season_id' }, function() {
this.resource('episodes', function() {
this.resource('episode', { path: '/:episode_id' });
})
});
});
});
});
});
How would I link to a specific episode using the linkTo helper that Handlebars provides? In other words, how does Ember figure out what the other parameters of the URL should be, that is, the serie_id and episode_id? The documentation states that I should pass an episode model to the episode route as shown below.
{{#linkTo "episode" episode}}
This is to link to the following URL structure.
/series/:serie_id/seasons/:season_id/episodes/:episode_id/
When I use the linkTo helper like that, Ember throws an error telling me that it cannot call get with id on undefined. I assume that it uses the episode model to figure out what the serie_id and episode_id are and my guess is that the model needs to conform to a specific convention (structure or blueprint) for Ember to find these ids.
These are the aspects that I find most difficult about Ember. It isn't very transparent even if you use Ember in debug mode. Any pointers or references are much appreciated.
UPDATE 1: After some digging, I found out that the route's serialize method is a key element in accomplishing this. However, when I use the linkTo helper as illustrated above, the model passed to the route's serialize method is undefined for some reason (even though it is not when passed to the linkTo helper. The question that led to this discovery can be found here.
UPDATE 2: It turns out that the serieSeason route's serialize method receives the wrong model, an episode instead of a season, when the link is generated. It isn't clear, though, why it is receiving the wrong model. Where does the model parameter of the serialize method come from?
UPDATE 3: The linkTo helper works fine if I return static data from the serialize method of each route involved, which means that the linkTo helper isn't involved in the problem.
It turns out that the answer could be found in the properly documented source of Ember ... because that is what one does after searching the web for several days.
The answer is simple. The linkTo helper accepts more than one model. For each dynamic segment of the destination URL, you pass a corresponding model. Each passed model will become the model of the corresponding route in the destination URL. In the example that I describe above, this results in the following.
{{#linkTo "episode" serie season episode}}
The serie model will be passed to the serie route, the season model to the season route, and the episode model to the episode route. What confuses many developers is that the route's model hook isn't triggered when you use the linkTo helper. This isn't too surprising if you realize that the developer provides (or can provide) the model for the corresponding route by passing one or more models (or zero).
Because there isn't much documentation for deeply nested resources, it wasn't trivial to find out how the linkTo helper does its job under the hood. Diving in Ember's source definitely helps getting up to speed with the framework.

Transition from one route to another with a different model in Emberjs

I have a search page where we are getting different types of search results. In the list of search results I would like to use
{{#linkTo 'someResources.someResource' result}}{{result.Name}}{{/linkTo}}
And on the route someResources.someResource I want to use a totally different model than on the search page. How do I do that? When I click on the link for the linkTo it doesn't load the model again, instead it tries to use the model named result here.
So what I would like to do is to reload the model when I navigate to someResources.someResource based on the values in result.
The I do have a model named App.SomeResource and a find method for it that works if I go directly to that page.
Ember will bypass the model() hook when using linkTo as you've discovered. The assumption is that you passed a model to it, so it and will use that(result) as the model.
The next hook you can use is setupController. Since you have a model hook that works on the direct route, you can use call it directly from here.
One caveat is that you need to also allow for the direct route loading where the model will already have loaded.
setupController: function(controller, model) {
if (!model.isModel) {
this.model().then(function(result)) {
controller.set('model', result)
}
}
}
model.isModel is this check via an isModel property on the directly loaded model, which should be absent when passed with linkTo.
Note: the above code assumes that you are returning a Promise in your model() hook.
Since the problem is that I want a full reload of the model when doing the transition using linkTo won't work since that is using the model given to it. The solution to the problem is actually quite simple, just use a regular html a-tag instead. What I ended up doing was this:
<a {{bindAttr href="somePropertyInYourModel"}}>{{someTextProperty}}</a>
The property somePropertyInYourModel is a property containing the url to the new page. If the url is in the ember routes it will be as if you where typing that address in the address bar and pressing enter, but without the full reload of the page.
I think this is something that could be improved in ember, it would be much nicer if I could write something like:
{{#linkToRoute "resourceA.routeB" params="val1,val2,val3"}}Go here{{/linkToRoute}}
given I have this routes set up:
App.Router.map(function() {
this.resource("resourceA", {{path: "/resourceA"}}, function() {
this.route("routeB", {{path: "/:prop1/:prop2/:prop3");
}
});
I would like to get:
Go here
The order of the val1,val2,val3 matters, if the order is changed they should also be changed in the final url.