Use of serialize hook in ember routes - ember.js

What is the use of serialize hook in ember route class?
App.PostRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('post', params.post_id);
},
serialize: function(post) {
return { post_id: post.get('id') };
}
});
Ember Documentation says:
If your dynamic segment ends in _id, the default model hook will convert the first part into a model class on the application's namespace (post becomes App.Post). It will then call find on that class with the value of the dynamic segment.
The default serialize hook will pull the dynamic segment with the id property of the model object.
But i am not able to understand the use of serialize hook in route class

The serialize method determines what to use as parameter for the provided entity.
Example.
Say you have the following user model, with the following properties.
id
username
email
Now if you have a list of users, and you want to link to a show user details page, you might use a loop like this.
{{#each users}}
{{#link-to user.show this }} {{username}} {{/link-to}}
{{/each}}
So when Ember sees this link-to helper i will convert it to a link, which might look like this
elvar
Now the default behavior here is to use the id as a parameter, which is what your own example shows, i picks the id from the model. We can change this with the serializer method.
Lets say that instead of using the id, we want to use the username as a parameter.
App.UserShowRoute= Ember.Route.extend({
model: function(params) {
return this.store.find('user', params.user);
},
serialize: function(user) {
return { user: user.get('username') };
}
});
Now the link-to helper will yield the following instead.
elvar

Related

Params empty in model hook, but paramsFor is not empty

The example code below is how the model hook is supposed to work by default. Strangely, if I don't include the model hook at all, the model is populated correctly. If I include the model hook as below, it doesn't work because "params" is an empty object. However, this.paramsFor('somemodel') returns {somemodel_id: "1"} So, what am I missing here?
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
return this.store.find('somemodel', params.somemodel_id);
}
});
Nested routes inherit the parent route's model if you do not specify a model hook. If all you are doing is looking up the model to edit you don't need a model hook, if you are querying the store for something else and need access to somemodel you can access it via this._super(...arguments).
export default Ember.Route.extend({
model: function(params) {
return this.store.find('somemodel', this._super(...arguments).get('id'));
}
});
It seems that params don't propagate to nested routes. My router looks like this:
this.route('somemodel', { path: '/somemodels/:somemodel_id' }, function() {
this.route('edit');
});
The "index" route is implied and is the route that receives the params. The edit route is nested and does not receive the params.

Emberjs: controller and route not getting along

The example will represent it best, I suppose. So, I have a defined route with a model property, which displays the appropriate image, based on id in url. This worked:
App.DetailsRoute = Ember.Route.extend({
model: function(params) {
return App.Images.find(params.image_id);
}
});
However, I wanted to add an action and... when I set up the controller, the page did not have access to the model part. Controller:
App.DetailsController = Ember.Controller.extend({
saveToServer: function(){
//alert(JSON.stringify());
alert('hi');
}
});
So at this time it's like this: either the model is set and stuff gets displayed and controller doesn't work OR controller works and model not.
Why is this happening?

Nested routes loads the parent model

I have a nested route structure like this:
App.Router.map(function() {
this.resource('user', {path: '/user/:user_id'}, function() {
this.route('followers', {path: '/followers'});
});
});
when I hit the user/123/followers route I would expect that it automatically fetch the model from user/123/followers, but it just fetches the user model from user/123 again. What do I need to add so it fetches the right data for the route?
Each route have your own model, and this isn't propaged, by default.
So App.UserRoute model, returns the current model like expected:
App.User.find(params.user_id)
But because App.UserFollowersRoute have your own model hook, then you have to provided it.
You can do this easily using the modelFor.
App.UserFollowersRoute = Ember.Route.extend({
model: function() {
return this.modelFor('user');
}
});
The modelFor look for a model from a named route. So modelFor('user'), will retrieve the model from App.UserRoute.
And in your user/followers template, you will have the current user, in the current context:
<script type="text/x-handlebars" data-template-name="user/followers">
<h2>{{name}} followers:</h2>
<ul>
{{#each followers}}
<li>{{name}}</li>
{{/each}}
</ul>
</script>
Here a sample with this working
Ember will automatically call User.find(123) when you hit /user/123/... because that is the default model hook for App.UserRoute. If you want to fetch additional data when the followers route is accessed, define a model hook for App.UserFollowersRoute:
App.UserFollowersRoute = Ember.Route.extend({
model: function() {
user = this.controllerFor('user');
// Now find and return the list of followers
}
});

Ember (data) linkTo without passing model, just fetch new model

When using the linkTo helper in a handlebars template, Ember sets up the correct URL for the link with the help of the serializer I have added to the route:
serialize: function(slug, params) {
var name, object;
object = {};
name = params[0];
object[name] = slug;
return object;
}
And when I click the link, Ember transitions to the correct page with the correct slug and everything, but it doesn't have the correct data, and it says that. I believe it's because what I pass to my linkTo statement as second parameter is just the slug and not the whole model.
Is it possible to get Ember to simply fetch the data as it would if I just typed the URL into the address bar instead of relying on the model (that is not) passed to the linkTo statement?
UPDATE
I have tried this inside the activate method on my route, but now it seems the problem is that the rendering has to wait until this is done.
activate: function() {
this.context.isLoaded = false;
this.model(this.context.query.slug);
}
Any ideas? Maybe even with a prettier solution?
The solution I came up with at last, with help from some guys on IRC, was to use the setupController hook, like you mention, Darshan, and the serializer like this:
CustomRoute = Ember.Route.extend({
setupController: function(controller, model) {
var modelName = this.routeName.substr(0, 1).toUpperCase() + this.routeName.substr(1),
slug = model;
if (model.hasOwnProperty('slug'))
slug = model.slug;
controller.set('model', App[modelName].find({'slug': slug}));
},
serialize: function(slug, params) {
var name, object;
object = {};
name = params[0];
object[name] = slug;
return object;
}
});
This way, you can supply just the slug of the route as the second parameter to the linkTo helper instead of a model, and the serializer will set the URL up properly, and then the setupController will check if the model has a property slug, which properly means it's a proper model, and if it does not, it just guesses that the model is simply the slug, and then it will use the DS.Model.find method to return a promise to the controllers model store.
Because setupController is called everytime a route is entered, where as the model hook is only called sometimes, the DS.Model.find method will be used everytime to fetch the data via the promise, and voila - fetch data each time you enter a route.
This assumes that you use Ember.Data and that your model object is called App.*route name* starting with a capital letter, but it can easily be modified to fit whatever need one might have.
For all of the routes in my app I now subclass (extend) from this route thus getting my desired behaviour for all of my routes.
You can try using the slug name in the Route and then loading data for the Route using findQuery instead of find.
App.Router.map(function() {
this.resource('product', { path: '/product/:slug' });
});
App.ProductRoute = Ember.Route.extend({
model: function(params) {
return App.Product.query({name:params.slug});
}
});

Specify controller and model on a view that doesn't have a router

I have a simple app that is using ember-data to load from a RESTful web service and display the data using the route to specify the model.
App.AreasRoute = Ember.Route.extend({
model: function() {
return App.Area.find();
}
});
Then I can render the data in the handlebars template using {{#each content}}
But I want to have a second area of data from a different model displayed on the page as well. I started by creating a View and then rendering the view as part of my application template using {{view App.AnotherView}} which correctly renders the view.
However, this view doesn't have a route and therefore I have nowhere to tell it where to get it's data from. How do I do this?
The best place to load additional content is in the setupController hook of the route.
For example, if you define your route as
App.AreasRoute = Ember.Route.extend({
model: function() {
return App.Area.find();
},
setupController: function(controller) {
controller.set('someArea', App.Area.find('area_id'));
}
});
Then the Area model with id area_id can be accessed by {{someArea}} in your template.