Introduction To Ember Notes
If a route doesn’t have a model it looks to its parent route for its model (by default nested routes inherit the parent’s model).
this.resource(‘album’, {path: ‘/album/:album_id’}, function() {
this.resource(‘album.comments’);
});
this.resource(’song’, {path: ‘/song/:song_id’}, function() {
this.resource(’song.comments’);
});
Then must use namespaced route name in link-to helpers and put route files in directories reflecting the same namespace.
{{#link-to 'album.comments'}}See the album comments{{/link-to}}
{{#link-to 'song.comments'}}See the album comments{{/link-to}}
Inside app/models/user.js
import DS from 'ember-data'
var attr = DS.attr,
hasMany = DS.hasMany;
export default DS.Model.extend({
name: attr('string'),
age: attr('number'),
preferredMember: attr('boolean'),
posts: hasMany('post')
});
Inside app/models/post.js
import DS from 'ember-data'
var attr = DS.attr,
belongsTo = DS.belongsTo;
export default DS.Model.extend({
name: attr('string'),
body: attr('string'),
user: belongsTo('user')
});
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
this.store.find('user', 1); // Ember Data makes a GET request to /users/1
}
});
Post index page where we want to see all of the posts together.
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
this.store.find('post'); // using find w/ out an id makes a GET request to /posts
this.store.find('post', {page: 2, per_page: 20}); // GET /posts?page=2&per_page=20
}
});
Helpers and components use hyphenated names to differentiate themselves from properties and to adhere to the current W3C specification for custom elements which require element names with hyphens.
Template
<div class='field'>
<label>Your Name</label>
{{input type='text' value='name'}}
</div>
<div class='field'>
<label>First Name</label>
{{input type='text' value='firstName'}}
</div>
<div class='field'>
<label>Last Name</label>
{{input type='text' value='lastName'}}
</div>
<div class='field'>
<label>Secret Name</label>
<p>{{format-secret name}}</p> // using custom helper
<p>{{reverseNames this}}</p> // using Ember Object (user model)
</div>
Custom Helper
import Ember from 'ember';
function formatSecret(value) {
if (!value) { return ''; }
return value.split('').reverse().join('');
}
export default Ember.Handlebars.makeBoundHelper(formatSecret)
Custom Helper with Ember Object
import Ember from 'ember';
function reverse(value) {
if (!value) { return ''; }
return value.split('').reverse().join('');
}
function reverseNames(user) {
var names = [user.get('firstName'), user.get('lastName')];
return names.map(reverse).join(' ');
}
export default Ember.Handlebars.makeBoundHelper(reverseNames, 'firstName', 'lastName')
Template
<div class='left'>
<label>Post Body</label>
{{textarea value=body}}
</div>
<div class='right'>
<label>Preview</label>
<div class='preview'>
{{format-markdown body}}
</div>
</div>
<div class='row'>
<button {{action 'save' model}}>Save</button>
</div>
Custom Helper returning HTML
/* global marked */ // -> Indicates use of global variable 'marked' to make dependencies clear and JSHint happy
import Ember from 'ember';
function formatMarkdown(text) {
if (!value) { return ''; }
return marked(text).htmlSafe();
}
export default Ember.Handlebars.makeBoundHelper(formatMarkdown)
Ember introduces an object model on top of JavaScript's builtin prototypal inheritance (why we have to use getters).
Album = Ember.Oject.extend({
})
Album.create() // to instantiate
If details about how a property is managed leak into other parts of our application it means we have poor encapsulation.
To update the DOM when a model's property changes Ember uses an abstraction called computed properties which use an observer pattern to always keep the view layer up to date.
'assignees.length'
.export default Ember.object.extend({
isReadyForWork: function() {
return this.get('assignee') && this.get('description');
}.property('assignee', 'description')
});
Ember Object improves JavaScript's object model in the following ways.
@each syntax in computed properties (used to manage lists of items)
function(){}.property('keyForArray.@each.propertyOnEachItem')
Example: 'tasks.@each.isReadyForWork' means
Computed macros express common computed properties in a concise way.
Naming Convention = {{name-of-component}}
The run loop lets ember officially schedule the work your application does in different queues to get the best performance and smoothest rendering.
run(function() {
// code to execute within a RunLoop
});
Queues
Wrapping a function in run.debounce means it will be called in a certain amount of time as long as the function is not invoked again.
run.debounce(this, myFunc, 1000);
// wait 500ms
run.debounce(this, myFunc, 1000);
// myFunc invoked after 1 second (after the second time it was invoked)
Examples of what you might use services for
Services can be injected into other ember objects using the injected property syntax.
export default Ember.Service.extend({
serviceName: Ember.inject.service('service-name');
setup: function() {
this.get('serviceName') // If no part of the object 'gets' the service then it will never be created
}.on('init')
})
Unit Tests
Integration Tests
End to End Tests (acceptance tests / feature tests)