This is a collection of best practices we've found useful when developing Ember applications at AlphaSights. You're incouraged to follow them.
First of all, start with the JavaScript Style Guide.
Read the official guide.
Everything apart from components has to be laid out based on the router
Router.map(function() {
this.route('dashboard', { path: '/' });
this.route('performance');
this.route('projects');
this.route('team', function() {
this.route('team.project', { path: ':project_id' });
});
this.route('application_error', { path: '*path' });
});
Use pods. Every pod should follow this structure:
app/pods/projects/controller.js
app/pods/projects/route.js
app/pods/projects/template.hbs
Use the appropriate Ember Data methods for retrieving data
Type | Async from server/store | Sync from store | Query server |
---|---|---|---|
Single Record | findRecord(type,id) | peekRecord(type, id) | queryRecord(type, {query}) |
All Records | findAll(type) | peekAll(type) | query(type, {query}) |
Use computed properties whenever applicable.
Use Ember.computed.oneWay
instead of Ember.computed.alias
unless there is a
specific reason for propagating changes back to the source.
Prefer using the on
syntax for events like didInsertElement
instead of
overriding the method directly. This spares you from having to call super
and
gives you a chance to give a meaningful name to the method.
Do this:
setupFoundation: Ember.on('didInsertElement', function() {
Ember.$(document).foundation({ dropdown: {} });
}),
Don't do this:
didInsertElement: function() {
this._super.apply(this, arguments);
Ember.$(document).foundation({ dropdown: {} });
}
Use camel case for action properties.
Do this:
{{as-star-project model=project onSave=(action "saveProject")}}
Don't do this:
{{as-star-project model=project onsave=(action "saveProject")}}
Use closure actions whenever applicable.
Prefer
<!-- template.hbs -->
{{as-star-project model=project onSave=(action "saveProject")}}
<!-- as-star-project.hbs -->
<button {{action this.attrs.onSave}}>Save</button>
Over
<!-- template.hbs -->
{{as-star-project model=project onSave="saveProject"}}
<!-- as-star-project.hbs -->
<button {{action "save"}}>Save</button>
<!-- as-star-project.js -->
Ember.Component.extend({
actions: {
save: function() {
this.sendAction('onSave');
}
}
});
When using closure actions, access the passed closure using this.attrs
<!-- template.hbs -->
{{as-rate-project model=project onRate=(action "rateProject")}}
<!-- as-rate-project.hbs -->
<input type="text" oninput={{action "rate" value="target.value"}} />
<!-- as-star-project.js -->
Ember.Component.extend({
actions: {
rate: function(value) {
this.attrs.onSave(value);
}
}
});
Use assert.expect
in every test.
Do this:
test('1 is 1', function(assert) {
assert.expect(1);
assert.equals(1, 1);
});
Don't do this:
test('1 is 1', function(assert) {
assert.equals(1, 1);
});
If you want to perform multiple actions and assertions, remember to nest the
andThen
functions.
Do this:
test('1 is 1', function(assert) {
assert.expect(2);
click('button.do');
andThen(function() {
assert(find('.done').length, 1);
click('button.undo');
andThen(function() {
assert(find('.done').length, 0);
});
});
});
Don't do this:
test('1 is 1', function(assert) {
assert.expect(2);
click('button.do');
andThen(function() {
assert(find('.done').length, 1);
});
click('button.undo');
andThen(function() {
assert(find('.done').length, 0);
});
});
Remember that every action like click, select and fillIn are promises, so you
just need one andThen
function to wait for all the promises to resolve.
Do this:
test('1 is 1', function(assert) {
assert.expect(1);
click('button.add');
fillIn('input[name=title]', 'Hello world');
select('.priority', 'High');
click('button.submit');
andThen(function() {
assert(find('.todos ul li').length, 1);
});
});
Don't do this:
test('1 is 1', function(assert) {
assert.expect(1);
click('button.add');
andThen(function() {
fillIn('input[name=title]', 'Hello world');
andThen(function() {
select('.priority', 'High');
andThen(function() {
click('button.submit');
andThen(function() {
assert(find('.todos ul li').length, 1);
});
});
});
});
});
Use double quotes for strings.
Whenever you need a wrapper for a certain element, avoid creating a class name
like element-wrapper
. Instead, add the element class to the wrapper element
and have an anonymous element inside.
Do this:
<div class="message">
<div>
</div>
</div>
Don't do this:
<div class="message-wrapper">
<div class="message">
</div>
</div>
Use a single line when the parameter list is short.
Do this:
{{as-star-project model=project onSave=(action "saveProject")}}
Don't do this:
{{as-star-project
model=project
onSave=(action "saveProject")}}
Use "Clojure" style formatting when the parameter list is very long.
Do this:
{{as-quick-jump-result-content
title=name
details=accountName
resourceId=id
resourcePath="client/contacts"}}
Don't do this:
{{
as-quick-jump-result-content
title=name
details=accountName
resourceId=id
resourcePath="client/contacts"
}}
Use .
as a separator for the Ember resolver. Use /
only for templates.
Do this:
export default Ember.Component.extend({
layoutName: 'components/as-widget/widget',
});
Don't do this:
export default Ember.Component.extend({
layoutName: 'components.as-widget.widget',
});
Prefix global variable names with the file/directory name, i. e. if you're
adding a variable to templates/team/_project.scss
, make sure to prefix it with
team-project
.
Do this:
$team-project-header-height: rem-calc(40);
Don't do this:
$header-height: rem-calc(40);
If you have to handle optional classes for a certain selector, put the styles for those at the end.
Do this:
.project-list-item {
padding: 5px;
> div {
color: white;
}
&.no-target {
background-color: red;
}
}
Don't do this:
.project-list-item {
padding: 5px;
&.no-target {
background-color: red;
}
> div {
color: white;
}
}