EmberJS
Yet Another Javascript Thingamajig?
- Angular: https://angularjs.org/
- React: https://facebook.github.io/react/
- Backbone: http://backbonejs.org/
AngularJS vs. Backbone.js
vs. Ember.js vs React
www.airpair.com/js/javascript-framework-comparison
Ember: Holistic approach, embraces MVC structure
Application performance. Saves from writing boilerplate. Favoring convention over configuration
Backbone: Minimalism. Small, fast & easy to learn. Provides the minimum (and at times even less than minimum)
Angular: innovative approach for extending HTML. Google behind it. Works well both for quick prototyping projects and large-scale production applications
AngularJS vs. Backbone.js
vs. Ember.js vs React
smashingboxes.com/blog/choosing-a-front-end-framework-angular-ember-react
www.sitepoint.com/react-has-won-the-client-side-war/
www.developereconomics.com/comparison-4-popular-javascript-mv-frameworks-part-2/
React: lightest of the three (Angular/Ember/React). It can’t even be considered a framework. It does one thing really well: render UI components. Many pair it with Ember/Angular. Common scenario is to use it with a Flux architecture
Trends
Ember: Getting Started
npm install -g ember-cli
ember new my-app
Auto-Updating Handlebars Templates
Ember makes Handlebars templates even better, by ensuring your HTML stays up-to-date when the underlying model changes. To get started, you don't even need to write any JavaScript.
<div class="post">
<h1>By {{author}}</h1>
<div class="body">{{body}}</div>
<h1>Comments</h1>
{{#each comments}}
<h2>By {{author}}</h2>
<div class="body">{{body}}</div>
{{/each}}
</div>
Components
Create your own application-specific HTML tags, using Handlebars to describe their markup and JavaScript to implement custom behavior
<!-- application.js -->
<ul class="example-gravatar">
<li>{{gravatar-image email="tomster@emberjs.com" size="200"}}</li>
<li>{{gravatar-image size="200"}}</li>
</ul>
// components/gravatar-image.js
App.GravatarImageComponent = Ember.Component.extend({
size: 200,
email: '',
gravatarUrl: Ember.computed('email', 'size',
function() {
var email = this.get('email').toLowerCase(),
size = this.get('size');
return 'http://www.gravatar.com/avatar/' + md5(email) + '?s=' + size;
})
});
<!-- components/gravatar-image.hbs -->
<img src={{gravatarUrl}}>
<div class="email-input">
{{input type="email" value=email placeholder="Enter your Gravatar e-mail"}}
</div>
Core Concepts
Router
When the URL changes, the Ember router maps the URL to a route handler
Route Handlers
- Renders a template
- Loads a model that is available to the template
Models
Represent persistent state
Components
- Control how the user interface behaves
-
Consist of a
- template written in Handlebars
- source file written in JavaScript defining behavior
Templates
- Describe how a user interface looks
- Organizing the layout of HTML in an application
Tutorial - Create and Start
# create new project
ember new super-rentals
# project structure
|--app
|--bower_components
|--config
|--dist
|--node_modules
|--public
|--tests
|--tmp
|--vendor
bower.json
ember-cli-build.js
package.json
README.md
testem.json
# start server
ember server
Tutorial - Generate Routes
# create route
ember generate route about
# create another route
ember g route contact
# create main default route
ember g route index
# link pages
{{#link-to "about"}}About{{/link-to}}
# custom path for routes
this.route('bgm', {path: 'b'});
Tutorial - Route Handlers
// load model in route handler
var rentals = [{
id: 1, title: 'Grand Old Mansion', owner: 'Veruca Salt',
city: 'San Francisco', type: 'Estate', bedrooms: 15,
image: 'https://upload.wikimedia.org/wikipedia/commons/c/cb/Crane_estate_(5).jpg'
}, {
id: 2, title: 'Urban Living', owner: 'Mike TV',
city: 'Seattle', type: 'Condo', bedrooms: 1,
image: 'https://upload.wikimedia.org/wikipedia/commons/0/0e/Alfonso_13_Highrise_Tegucigalpa.jpg'
}, {
id: 3, title: 'Downtown Charm', owner: 'Violet Beauregarde',
city: 'Portland', type: 'Apartment', bedrooms: 3,
image: 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Wheeldon_Apartment_Building_-_Portland_Oregon.jpg'
}];
export default Ember.Route.extend({
model() { // model hook called each time the index route is entered
return rentals;
},
});
<!-- update index -->
{{#each model as |rental|}}
<h2>{{rental.title}}</h2>
<p>Owner: {{rental.owner}}</p><p>Type: {{rental.type}}</p>
<p>Location: {{rental.city}}</p><p>Number of bedrooms: {{rental.bedrooms}}</p>
{{/each}}
Tutorial - Model
# generate model
ember g model rental
// define the model
export default DS.Model.extend({
title: DS.attr(),
owner: DS.attr(),
city: DS.attr(),
type: DS.attr(),
image: DS.attr(),
bedrooms: DS.attr()
});
Tutorial - Model
# install Mirage and restart ember server
ember install ember-cli-mirage
// app/mirage/config.js: specify /rentals endpoint
export default function() {
this.get('/rentals', function() {
return { data: [{
type: 'rentals', id: 1, attributes: {
title: 'Grand Old Mansion', owner: 'Veruca Salt', city: 'San Francisco', type: 'Estate',
bedrooms: 15,
image: 'https://upload.wikimedia.org/wikipedia/commons/c/cb/Crane_estate_(5).jpg'
}
}, {
type: 'rentals', id: 2, attributes: {
title: 'Urban Living', owner: 'Mike Teavee', city: 'Seattle', type: 'Condo',
bedrooms: 1,
image: 'https://upload.wikimedia.org/wikipedia/commons/0/0e/Alfonso_13_Highrise_Tegucigalpa.jpg'
}
}, {
type: 'rentals',id: 3, attributes: {
title: 'Downtown Charm', owner: 'Violet Beauregarde', city: 'Portland', type: 'Apartment',
bedrooms: 3,
image: 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Wheeldon_Apartment_Building_-_Portland_Oregon.jpg'
}}]};});}
// update model hook
model() {
return this.store.findAll('rental');
}
Tutorial - Component
# generate component
ember g component rental-listing
<!-- app/templates/components/rental-listing.hbs -->
<h2>{{rental.title}}</h2>
<p>Owner: {{rental.owner}}</p>
<p>Type: {{rental.type}}</p>
<p>Location: {{rental.city}}</p>
<p>Number of bedrooms: {{rental.bedrooms}}</p>
<!-- index -->
{{#each model as |rentalUnit|}}
{{rental-listing rental=rentalUnit}}
{{/each}}
Tutorial - Component
// app/components/rental-listing.js
export default Ember.Component.extend({
isImageShowing: false,
actions: {
imageShow() {
this.set('isImageShowing', true);
},
imageHide() {
this.set('isImageShowing', false);
}
}
});
<!-- app/templates/components/rental-listing.hbs -->
{{#if isImageShowing}}
<p><img src={{rental.image}} alt={{rental.type}} width="500"></p>
<button {{action "imageHide"}}>Hide image</button>
{{else}}
<button {{action "imageShow"}}>Show image</button>
{{/if}}
Computed Properties
Person = Ember.Object.extend({
// these will be supplied by `create`
firstName: null,
lastName: null,
fullName: Ember.computed('firstName', 'lastName', function() {
return `${this.get('firstName')} ${this.get('lastName')}`;
})
});
var ironMan = Person.create({
firstName: 'Tony',
lastName: 'Stark'
});
ironMan.get('fullName'); // "Tony Stark"
Observers
Person = Ember.Object.extend({
// these will be supplied by `create`
firstName: null,
lastName: null,
fullName: Ember.computed('firstName', 'lastName', function() {
return `${this.get('firstName')} ${this.get('lastName')}`;
}),
fullNameChanged: Ember.observer('fullName', function() {
// deal with the change
console.log(`fullName changed to: ${this.get('fullName')}`);
})
});
var person = Person.create({
firstName: 'Yehuda',
lastName: 'Katz'
});
// observer won't fire until `fullName` is consumed first
person.get('fullName'); // "Yehuda Katz"
person.set('firstName', 'Brohuda'); // fullName changed to: Brohuda Katz
Links
EmberJS
By Timothy Lim
EmberJS
- 2,090