Ember Routes How To? - ember.js

My problem is "simple", but I could not resolve it with Ember....
It is a Small Library App, with authors and books with those routes working fine
this.resource('books', function () {
this.route('edit', {path: '/:book_id/edit'});
this.route('show', {path: '/:book_id'});
});
this.resource('authors', function () {
this.route('new');
this.route('edit', {path: '/:author_id/edit'});
this.route('show', {path: '/:author_id'});
});
Now I want to add a route which allows me to register new books, using a link from a current author template /authors/156
The route must open a books/new template, and link the new book object with his author: ie I want to show <h1>New book from {{author.name}}</h1>
What route should I add to the existing ones?
How can I pass the author reference to the new book object?

I see three ways of doing it:
Put it under the books resource and require the author as a route parameter:
this.resource('books', function() {
this.route('new', { path: '/new/:author_id' });
});
Put the route under the books resource, but put the author in a query parameter instead.
this.resource('books', function() {
// Declare required query parameter on controller for `new` route
this.route('new');
});
Put the route under authors and require the author in the URL:
this.resource('authors', function() {
this.route('new_book', { path: '/:author_id/new_book' });
});
I would suggest the third option, as I think it's the cleanest. In your controller, you can create a new book fairly easily:
export default Ember.Controller.extend({
actions: {
createBook: function() {
var author = this.get('model');
var book = this.store.createRecord('book', {
author: author,
...
});
book.save();
}
}
});

I've tried and succeed with the second suggested method, query params.
Router:
this.resource('books', function () {
this.route('new');
this.route('show', {path: '/:book_id'});
};
Route
App.BooksNewRoute = Ember.Route.extend({
queryParams: {
author_id: {
refreshModel: true
}
},
model: function (params) {
var newBook = this.store.createRecord('book');
this.store.find('author', params.author_id).then(function (author) {
newBook.set('author', author);
});
return newBook;
}
});
and Controller
App.BooksNewController = Ember.ObjectController.extend({
queryParams: ['author_id'],
author_id: null,
actions: {
save: function () {
var controller = this;
this.get('model').save().then(function (book) {
controller.transitionToRoute('books.show', book);
}, function (error) {
console.error(error);
});
},
cancel: function () {
this.get('model').rollback();
this.transitionToRoute('index');
}
}
});

Related

Nested routes in Ember

I want my settings area to look like this:
..
/settings/:accountId/users
/settings/:accountId/users/:userId
I have my router defined as follows:
Router.map(function() {
this.route('login');
this.resource('settings', { path: 'settings/:settings_id' }, function() {
this.route('overview');
this.route('users');
});
});
This works for displaying the users listing page. I'm not sure how to take it to the next step though and have both a route and a resource for /users and /users/1.
Thanks.
In the latest versions of Ember, route's can have sub routes (for namespace sake).
Router.map(function() {
this.route('login');
this.resource('settings', { path: 'settings/:settings_id' }, function() {
this.route('overview');
this.route('users', function(){
this.route('user', {path:':user_id'});
});
});
});
http://emberjs.jsbin.com/cutayuniga/1/edit?html,js,output
If you're in an older version, you will have to make users a resource.
Router.map(function() {
this.route('login');
this.resource('settings', { path: 'settings/:settings_id' }, function() {
this.route('overview');
this.resource('users', function(){
this.route('user', {path:':user_id'});
});
});
});

How do I do a "needs" on a nested controller for emberjs?

I have the following router:
this.resource('foo', {path: '/foo'}, function() {
this.resource('foo.bar', {path: '/bar'}, function() {
this.route('wat', {path: '/wat'});
});
});
My FooBarWatController is a singleton and I need access to the FooBarController's model. What does the needs look like for FooBarWatController to have access to the FooBarController?
FooBarWatContoller = Ember.ObjectController.extend({
needs: ['?'],
action: {
something: function() {
console.log(this.get('controllers.?.model').get('id'));
}
}
});
FooBarWatContoller = Ember.ObjectController.extend({
needs: ['foo/bar'],
action: {
something: function() {
console.log(this.get('controllers.foo/bar.model').get('id'));
}
}
});

How to define a nested route to render when hitting the parent in Ember

I have a blog route, and a blog-post route.
Router:
App.Router.map(function () {
this.resource('blog', function () {
this.route('post', {path: ':id/:title'});
});
});
Routes:
App.BlogRoute = Ember.Route.extend({
model: function () {
return this.store.find('BlogPost');
}
});
App.BlogPostRoute = Ember.Route.extend({
model: function (params) {
return this.store.findById('BlogPost', params.id);
},
serialize: function (model, params) {
return {
id: model.get('id'),
title: Ember.String.dasherize(model.get('title'))
}
}
});
In my Handlebars template for the parent blog route I have an {{outlet}} that works fine when I click one of the {{#link-to}}s.
What I want to do is render by default the most recent (highest ID) blog post when a user goes to the /blog route.
I found this question and tried this as a result, to no avail:
App.BlogIndexRoute = Ember.Route.extend({
redirect: function () {
var latest = 3;
this.transitionTo('blog.post', {id: latest});
}
});
(latest is just a placeholder for this.model.pop() or whatever it needs to be.)
I just can't figure out how exactly to load the sub route with the data from the model.
You can fetch the model for any resource/route that has already been fetched (aka parent resources) using modelFor
App.BlogIndexRoute = Ember.Route.extend({
redirect: function () {
var blogs = this.modelFor('blog');
if(blogs.get('length')){
this.transitionTo('blog.post', blogs.get('firstObject')); // or blogs.findBy('id', 123)
}
}
});

Can Ember handle multiple routes to a single resource?

I want to show the profile page on my web app either by username or user_id. I've tried the following with no success, any ideas?
MyApplication.Router.map(function () {
this.resource('user', { path: '/user' }, function () {
this.resource('profile', {path: '/profile/id/:user_id'});
this.resource('profile', {path: '/profile/:user_username'});
});
this.resource('notFound', {path: '/notfound'});
});
It seems Ember shouldn't really be used in this way. I've read through issues 551 and 571 on Embers Github. I think it's best to stick with showing a user by id to avoid working against the framework.
You can merge those two resources into one as follows:
MyApplication.Router.map(function () {
this.resource('user', { path: '/user' }, function () {
this.resource('profile', {path: '/profile/id/:value'});
});
});
MyApplication.ProfileRoute = Ember.Route.extend({
model: function(params) {
var value = params.value;
if (isNaN(value)) {
return this.store.find('user', { username: value });
} else {
return this.store.find('user', value);
}
}
});
If the value in the URL is a number it will do a findById; otherwise it will do a findQuery. Obviously this approach will fail if some username is a number.
If the URL is the important part for you, you could change the resource names and keep the url prefix the same, for example:
MyApplication.Router.map(function () {
this.resource('user', { path: '/user' }, function () {
this.resource('profileByUserId', {path: '/profile/id/:user_id'});
this.resource('profileByUsername', {path: '/profile/:user_username'});
});
this.resource('notFound', {path: '/notfound'});
});
Hope it helps.
May be with nested routes for the profile resource
MyApplication.Router.map(function () {
this.resource('user', { path: '/user' }, function () {
this.resource('profile', function(){
this.route('byid',{path: '/id/:user_id'});
this.route('byname',{path: '/name/:user_username'});
});
});
this.resource('notFound', {path: '/notfound'});
});
But remember to implement serialize for the definition of the route with user_name
http://emberjs.com/guides/routing/defining-your-routes/
The previos aproach is also valid IMHO

How to access a parent model within a nested index route using ember.js?

I have the following route structure
App.Router.map(function(match) {
this.route("days", { path: "/" });
this.resource("day", { path: "/:day_id" }, function() {
this.resource("appointment", { path: "/appointment" }, function() {
this.route("edit", { path: "/edit" });
});
});
});
When I'm inside the AppointmentIndexRoute I'm looking for a way to create a new model using some meta day from the day (parent) model but because the day model does not yet know about this appointment I'm unsure how to associate them until the appointment is created / and the commit is fired off.
Any help would be much appreciated
From within the AppointmentIndexRoute's model hook you can use modelFor('day') to access the parent model. For example:
App.AppointmentIndexRoute = Ember.Route.extend({
model: function(params) {
day = this.modelFor("day");
...
}
});
Another example is here: emberjs 1.0.0pre4 how do you pass a context object to a resource "...Index" route?
What if I am not using ember data? How do I get the parent id in a route like
this.resource('workspace',function () {
this.resource('workflow', {path: '/:workspace_id/workflow'}, function () {
this.route('show', {path: '/:workflow_id'});
});
});
This code will not work:
App.WorkflowShowRoute = Em.Route.extend({
model: function(params) {
var ws = this.modelFor('workspace'); //ws is undefined
return this.store.find('workflow', params.id, ws.id);
}
});
EDIT:
I found a workaround, it's not ideal but works exactly the way I want it.
this.resource('workspace',function () {
this.route('new');
this.route('show', {path: '/:workspace_id'});
//workflow routes
this.resource('workflow', {path: '/'}, function () {
this.route('new', {path:'/:workspace_id/workflow/new'});
this.route('show', {path: '/:workspace_id/workflow/:workflow_id'});
});
});
And in my workflow route, I can access the workspace_id jus as I expect from the params property:
App.WorkflowShowRoute = Em.Route.extend({
model: function(params) {
return this.store.find('workflow', params.workflow_id, params.workspace_id);
}
});
Finally, here is my link-to inside the workspace.show route helper:
{{#each workflow in workflows}}
<li>
{{#link-to 'workflow.show' this.id workflow.id}}{{workflow.name}}{{/link-to}}
</li>
{{/each}}