Confused - Why is there both a resource.index route and a resource route?


#1

Why is there both a resource.index route and a resource route?

I have been trying to understand this for quite a while now and it is not clear to me. I am an AngularJS/Rails developer trying to make the switch to Ember on the front-end. So far, I am pretty frustrated with how difficult it is to understand how to best setup/organize a simple CRUD resource in Ember.

For example:

Router.map(function() {
    this.resource('users', function() {
        this.route('new');
        this.route('show', { path: '/:user_id' });
        this.route('edit', { path: '/:user_id/edit' });
    });
});

First of all, is this the recommended way to create a “standard” CRUD resource? One that works well with a Rails scaffolded resource as its API for example. Or should the resource be defined with the singular noun as in this.resource(‘user’)?

More importantly, which route handler should have the model hook? It works with the model hook put in either the UsersRoute or the UsersIndexRoute, but with some unexpected behavior in my opinion.

Option #1 - Use the UsersRoute

export default Ember.Route.extend({
	model: function() {
		return this.store.find('user');
	},
	actions: {
		save: function() { ... }
	}
});

This works fine but hitting the /users/:user_id URL makes a GET request to /users, so the server response contains all of the users. Also, this option creates a strange directory structure as follows:

app/routes/users/new.js
app/routes/users.js

Option #2 - Use the UsersIndexRoute

export default Ember.Route.extend({
	model: function() {
		return this.store.find('user');
	},
	actions: {
		save: function() { ... }  //Doesn't work from users/new or users/edit
	}
});

This works fine as well but I am not sure why the model is inherited in the UsersNewRoute and UsersEditRoute. Is the UsersIndexRoute considered a parent route? It does not appear to be in the Ember Inspector. Lastly, why does the save action not work from here?

Any advice here will be greatly appreciated. I have always been attracted to Ember for its convention-over-configuration approach but this index route stuff does not seem to fit the bill.


#2

I’m relatively new to Ember too, but I’ll try my best to answer your questions.

The biggest hurdle for myself was that how you define your routes isn’t really as clear cut as it is in something like Rails. The purpose of route nesting in Ember is if you wish to render both the parent route and the sub routes at the same time on the screen. In your example I’d imagine it would be if you wish to always show a list of users in addition to the sub routes. Visually this would be something like this:

+------------------+--------------------------------+
|    Users Route   |          Users Outlet          |
|                  |                                |
|                  |                                |
|                  |                                |
|                  |                                |
|                  |       Sub routes get           |
|                  |       rendered in here         |
|                  |                                |
+------------------+--------------------------------+

Navigation between the sub routes would not remove the content in the users list from the screen.

I don’t think it’s a good idea to draw direct mappings with the routing in Ember and Rails. The routing as I mentioned before should really be driven by how you want to display things in your UI.

If you wish to go the UI with the persistent user list, I’d place the model hook for all users in app/routes/users.js. The other routes would have something like this:

app/routes/users/edit.js

export default Ember.Route.extend({
  model: function(params) {
    return this.store.find("user", params.id) {
  }
});

app/routes/users/show.js

export default Ember.Route.extend({
  model: function(params) {
    return this.store.find("user", params.id) {
  }
});

These routes are required for page full page loads, as Ember won’t have the current user in the store. Linking to the edit or show routes in the app/templates/users.hbs template should be done by directly passing the user model to the route:

{{#each user in model}}
  {{#link-to "users.show" user}}
    {{user.name}}
  {{/link-to}}
{{/each}}

{{outlet}}

The index route will be a sibling route of the new, edit and show routes. Use of the index route in my UI example probably isn’t required, but this would again depend on what you want to show.

Hopefully this helps a bit, and hopefully I haven’t got anything wrong.


#3

Here’s what I’ve learned.

Any resource which has one or more routes beneath it will by default have an index route attached to it which is where the user will transition to when going to /resources e.g. this.transitionTo('resources.index'). By default if you do not provide your own template, it’s simply {{outlet}} but you can define your own template to replace it.

My standard CRUD setup for situations where I want to display the sub-route on the same page as the parent via {{outlet}} would be:

Router.map(function() {
    this.resource('users', function() {
        this.route('new');
        this.resource('users', {path: '/:user_id'}, function() {
            this.route('show');
            this.route('edit');
        }
    });
});

The Ember guide about routing is pretty self-explanatory:

http://emberjs.com/guides/routing/

Hope this helps.


#4

Thank you for the responses. I do understand the guides and the fact that a resource implicitly has an index route. However, in my opinion there is not clear guidance on how to do something as basic as a CRUD resource. Specifically, whether to use the resource.index or the resource route for the model hook. This choice has a significant impact on directory structure among other things. I am attracted to Ember’s convention over configuration philosophy, so I wish the convention was more clear here.

Also, I do not like the implicit nature of generated objects. Yes, it saves a bit of time but it hides important details of how Ember works in my opinion. The time savings is negligible, especially with the CLI’s code generators. I think the “exercise” of explicitly creating the needed components is well worth the small amount of time it takes.

I have decided to stick with AngularJS with my current project at this point. I will re-visit Ember in the future because I am impressed with Ember’s progress with the CLI and use of ES6 module syntax. AngularJS is behind in this regard.