When using Ember CLI it’s important to keep in mind that the Resolver changes some of the naming conventions you would typically use out of the box with Ember, Ember Data and Handlebars. In this section we review some of these naming conventions.
1 // app/adapters/application.js
2 import Ember from "ember";
3 import DS from "ember-data";
4
5 export default DS.RESTAdapter.extend({});
1 // app/components/time-input.js
2 import Ember from "ember";
3
4 export default Ember.TextField.extend({});
1 // app/controllers/stop-watch.js
2 import Ember from "ember";
3
4 export default Ember.ObjectController.extend({});
And if it’s a nested controller, we can declare nested/child controllers
like such: app/controllers/posts/index.js
.
1 // app/helpers/format-time.js
2 import Ember from "ember";
3
4 export default Ember.Handlebars.makeBoundHelper(function(){});
1 // app/initializers/observation.js
2 export default {
3 name: 'observation',
4 initialize: function() {
5 // code
6 }
7 };
Note: initializers
are loaded automatically.
1 // app/mixins/evented.js
2 import Ember from "ember";
3
4 export default Ember.Mixin.create({});
1 // app/models/observation.js
2 import DS from "ember-data";
3
4 export default DS.Model.extend({});
1 // app/routes/timer.js
2 import Ember from "ember";
3
4 export default Ember.Route.extend({});
Nested routes as such: app/routes/timer/index.js
or app/routes/timer/record.js
.
1 // app/serializers/observation.js
2 import DS from "ember-data";
3
4 export default DS.RESTSerializer.extend({});
1 // app/transforms/time.js
2 import DS from "ember-data";
3
4 export default DS.Transform.extend({});
1 // app/utils/my-ajax.js
2 export default function myAjax() {};
<!-- app/index.hbs -->
{{view 'stop-watch'}}
1 // app/views/stop-watch.js
2 import Ember from "ember";
3
4 export default Ember.View.extend({});
And views, which can be referenced in sub-directories, but have no inheritance.
<!-- app/index.hbs -->
{{view 'inputs/time-input'}}
1 // app/views/inputs/time-input.js
2 import Ember from "ember";
3
4 export default Ember.TextField.extend({});
It is important to keep in mind that the Resolver uses filenames to create the associations correctly. This helps you by not having to namespace everything yourself. But there are a couple of things you should know.
1 // models/user.js
2 import Ember from "ember";
3 export default Ember.Model.extend();
// controllers/sign-up.js
import Ember from "ember";
export default Ember.Controller.extend();
If you prefer to nest your files to better manage your application, you can easily do so.
1 // controllers/posts/new.js results in a controller named "controllers.posts/new"
2 import Ember from "ember";
3 export default Ember.Controller.extend();
You cannot use paths containing slashes in your templates because Handlebars will translate them back to dots. Simply create an alias like this:
1 // controllers/posts.js
2 import Ember from "ember";
3 export default Ember.Controller.extend({
4 needs: ['posts/details'],
5 postsDetails: Ember.computed.alias('controllers.posts/details')
6 });
<!-- templates/posts.hbs -->
<!-- because {{controllers.posts/details.count}} does not work -->
{{postsDetails.count}}
Let’s say we were using Ember out of the box with the following view:
import Ember from "ember";
App.UserView = Ember.View.extend({});
We could easily embed this view into a container/parent using the Handlebars view helper:
{{view App.UserView}}
This is great. However, Ember CLI customizes the default Ember Resolver to help alleviate the issue of namespacing your objects (views, controllers, models, etc.) manually. The above example, as such, will not work in an Ember CLI project.
In Ember CLI our view would be declared like so:
1 // app/views/user.js
2 import Ember from "ember";
3 export default Ember.View.extend({});
We can then embed our view using the following convention:
{{view "user"}}
Note, that we did not namespace UserView
. The resolver takes care of this for you.
For more information about the default Ember resolver, check out the source here.
Test filenames should be suffixed with -test.js
in order to run.
As your app gets bigger, a feature-driven structure may be better. Splitting your application by functionality/resource would give you more power and control to scale and maintain it. As a default, if the file is not found on the POD structure, the Resolver will look it up within the normal structure.
In this case, you should name the file as its functionality. Given a resource Users
, the folder structure would be:
app/users/controller.js
app/users/route.js
app/users/template.hbs
Rather than hold your resource folders on the root of your app you can define a POD path using the attribute podModulePrefix
within your environment configs. The POD path should use the following format: {appname}/{poddir}
.
1 // config/environment.js
2 module.exports = function(environment) {
3 var ENV = {
4 modulePrefix: 'my-new-app',
5 podModulePrefix: 'my-new-app/pods' // namespaced directory where resolver will look for your resource files
6 environment: environment,
7 baseURL: '/',
8 locationType: 'auto',
9 //...
Then your folder structure would be:
app/pods/users/controller.js
app/pods/users/route.js
app/pods/users/template.hbs