Introducing Ember.js & ember-cli
David Thompson
Web application framework
Opinionated
Designed to communicate with an API
Ember-Data is an optional add-on to work with API data
Broccoli: asset compilation
QUnit: write tests
Testem: run tests
Babel: ES 2015 support
Express: local server
ember s
ember t --server
https://developer.marvel.com
Complex JSON objects in a non-standard format
{
"posts": [
{
"id": 1,
"title": "Ember is great",
"author": "David Thompson"
},
{
"id": 2,
"title": "I am bored",
"author": "Chad"
}
]
}
{
"post": {
"id": 1,
"title": "The Name of the Rose",
"author": "Umberto Eco"
}
}
/posts
/posts/1
DS.RESTAdapter
{
"code": 200,
"status": "Ok",
"copyright": "© 2015 MARVEL",
"attributionText": "Data provided by Marvel. © 2015 MARVEL",
"attributionHTML": "<a href=\"http://marvel.com\">Data provided by Marvel. © 2015 MARVEL</a>",
"etag": "dec2c14b0d86d0abb13a402343744796cefb4b84",
"data": {
"offset": 0,
"limit": 20,
"total": 1485,
"count": 20,
"results": [
{
"id": 1011334,
"name": "3-D Man",
"description": "",
"modified": "2014-04-29T14:18:17-0400",
"thumbnail": {
"path": "http://i.annihil.us/u/prod/marvel/i/mg/c/e0/535fecbbb9784",
"extension": "jpg"
},
"resourceURI": "http://gateway.marvel.com/v1/public/characters/1011334",
"comics": {
"available": 11,
"collectionURI": "http://gateway.marvel.com/v1/public/characters/1011334/comics",
"items": [
{
"resourceURI": "http://gateway.marvel.com/v1/public/comics/21366",
"name": "Avengers: The Initiative (2007) #14"
},
{
"resourceURI": "http://gateway.marvel.com/v1/public/comics/24571",
"name": "Avengers: The Initiative (2007) #14 (SPOTLIGHT VARIANT)"
},
{
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Superhero</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{content-for 'head'}}
<link rel="stylesheet" href="assets/vendor.css">
<link rel="stylesheet" href="assets/superhero.css">
{{content-for 'head-footer'}}
</head>
<body>
{{content-for 'body'}}
<script src="assets/vendor.js"></script>
<script src="assets/superhero.js"></script>
{{content-for 'body-footer'}}
</body>
</html>
/app/index.html
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
export default Router.map(function() {
this.route('about');
this.resource('characters', function() {
this.route('show', {
path: ':character_id'
});
});
});
/app/router.js
/
/loading
/about
/characters
/characters/loading/
/characters/:character_id
Defining a path in the router means Ember looks for a matching Route, Controller, View, and Template
import Ember from 'ember';
export default Ember.Route.extend({
queryParams: {
query: {
refreshModel: true
},
offset: {
refreshModel: true
}
},
model: function(params) {
if (!params.query) {
return this.store.find('character', { offset: params.offset });
}
return this.store.find('character', { nameStartsWith: params.query, offset: params.offset });
}
});
/app/routes/characters/index.js
Load JSON from API into model
Handle query parameters
import Ember from 'ember';
export default Ember.Route.extend({
queryParams: {
query: {
refreshModel: true
},
offset: {
refreshModel: true
}
},
model: function(params) {
if (!params.query) {
return this.store.find('character', { offset: params.offset });
}
return this.store.find('character', { nameStartsWith: params.query, offset: params.offset });
}
});
/app/models/character.js
Load JSON from API into model
Handle query parameters
import Ember from 'ember';
export default Ember.ArrayController.extend({
queryParams: ['query', 'offset'],
query: null,
offset: 0,
increment: 20,
queryField: Ember.computed.oneWay('query'),
meta: function() {
return this.get("content.meta");
}.property('content.[]'),
actions: {
search: function() {
this.set('query', this.get('queryField'));
this.set('offset', 0);
},
next: function() {
// TODO: check if there are any more results - compare offset to total
var next = this.get('increment') + this.get('meta').offset;
this.set('offset', next);
}
}
});
/app/controllers/characters/index.js
Templates get properties from controller
Controllers handle events
"The role of the view in an Ember.js application is to translate primitive browser events into events that have meaning to your application."
<div class="clearfix mxn2">
<div class="col col-12 p2 mx-auto">
<div class="flex flex-wrap flex-stretch p1">
{{#each model as |character|}}
{{characters/character-card character=character}}
{{/each}}
</div>
</div>
</div>
/app/templates/characters/index.js
Use Handlebars to present data
Ember helpers for forms and UI elements
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
extract: function(store, type, payload, id, requestType) {
var results = {};
results['characters'] = payload.data.results;
delete payload.data.results;
results['meta'] = payload.data;
return this._super(store, type, results, id, requestType);
}
});
/app/seriaizers/character.js
import DS from 'ember-data';
export default DS.Transform.extend({
deserialize: function(serialized) {
if (serialized) {
serialized = serialized.path + "." + serialized.extension;
}
return serialized;
},
serialize: function(deserialized) {
return deserialized;
}
});
/app/transforms/thumbnail.js
Custom attribute available to models