Ember.js
The good
The bad
The questionable
The good
ConventionS & Structure
RAILS CORE EXPERIENCE/MATURity
Tooling: Ember CLI, Addons & Extension
$ ember new leipzigjs
// Setup environments + config
// App structure
// NPM install
// Bower install
$ ember s
// Watch
// Livereload
// Compile
// Jshint
$ ember s --proxy http://localhost:3000
// All the above
// Proxy api calls
$ ember t
$ ember t -s
$ ember t -m "IndexController"
$ ember build -prod
// ES6 transpile
// Sass, Less, Stylus, CoffeeScript etc.
// Uglify
// Minify
// Concat
// Fingerprinting of asssets + CDN URLs
// Sourcemaps (disabled in prod by default, enabled in dev)
$ ember install:addon addon-name
$ ember g [controller|route|component|acceptance-test|http-mock|...]
$ ember addon
The good
ConventionS & Structure
RAILS CORE EXPERIENCE/MATURity
Tooling: Ember CLI, Addons & Extension
COMMUNITY/DOCS (Versioned)/ECOsystem
Helpful ERROR MESSAGES
Test integration
// Unit test
moduleFor('controller:index', 'IndexController');
test('#add', function() {
var controller = this.subject();
equal(controller.add(2, 3), 5, 'should add the two given numbers');
});
// Acceptance test
var App;
module('Acceptance: Index page', {
setup: function() {
App = startApp();
},
teardown: function() {
Ember.run(App, 'destroy');
}
});
test('displays form after click on toggle button', function() {
expect(2);
visit('/');
andThen(function() {
equal(find('form').length, 0, 'no form shown');
click('.show-form');
equal(find('form').length, 1, 'form is shown');
});
});
The good
ConventionS & Structure
RAILS CORE EXPERIENCE/MATURity
Tooling: Ember CLI, Addons & Extension
COMMUNITY/DOCS (Versioned)/ECOsystem
Helpful ERROR MESSAGES
EVERYTHING YOU'LL EVER NEED (YAGNI?)
Test integration
Ember.copy()
Ember.isEmpty()
Ember.isBlank()
Ember.isPresent()
Ember.isNone()
Ember.isArray()
Ember.makeArray()
Ember.keys()
Ember.merge()
Ember.tryInvoke()
Ember.typeOf()
Ember.inspect()
Ember.run()
Array polyfills for map, forEach, filter, etc.
Array.contains()
Array.uniq()
Array.without()
Array.compact()
Array.invoke()
Array.addObject() / Array.removeObject()
String.fmt()
String.camelize()
String.loc()
String.underscore()
String.w()
RSVP.Promise()
RSVP.hash()
RSVP.all()
Ember.Mixin
Ember.CoreObject.extend()
The Bad
COMPONENTS ARE NOT INDEPENDENT
Data down actions up
App = Ember.Application.create();
App.Router.map(function() {
});
App.IndexController = Ember.Controller.extend({
actions: {
login: function(data) {
console.log(data);
alert('Logging in...');
}
}
});
App.MyLoginComponent = Ember.Component.extend({
actions: {
login: function() {
this.sendAction('action', this.getProperties('username', 'password'));
}
}
})
<script type="text/x-handlebars">
<h1>Login</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" id="index">
{{my-login action='login'}}
</script>
<script type="text/x-handlebars" id="components/my-login">
{{input value=username placeholder='Username'}}
{{input value=password placeholder='Password'}}
<button {{action 'login'}}>Login</button>
</script>
<script type="text/x-handlebars">
<h1>Login</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" id="index">
{{my-menu}}
</script>
<script type="text/x-handlebars" id="components/my-login">
{{input value=username placeholder='Username'}}
{{input value=password placeholder='Password'}}
<button {{action 'login'}}>Login</button>
</script>
<script type="text/x-handlebars" id="components/my-menu">
<h1>Menu</h1>
{{my-login action='login'}}
</script>
The Bad
COMPONENTS ARE NOT INDEPENDENT
God damn state issues
App = Ember.Application.create();
App.Router.map(function() {
this.route('other');
});
App.IndexController = Ember.Controller.extend({
actions: {
toggleForm: function() {
this.toggleProperty('showForm');
}
}
});
<script type="text/x-handlebars">
<h1>Login</h1>
<h2>{{link-to 'Homepage' 'index'}}</h2>
<h2>{{link-to 'Other page' 'other'}}</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" id="index">
{{#if showForm}}
{{my-login}}
{{else}}
<a {{action 'toggleForm'}}>Show form</a>
{{/if}}
</script>
<script type="text/x-handlebars" id="other">
<h3>Other</h3>
</script>
<script type="text/x-handlebars" id="components/my-login">
{{input value=username placeholder='Username'}}
{{input value=password placeholder='Password'}}
<button {{action 'login'}}>Login</button>
</script>
The Bad
Performance (On Load)
God damn state issues
COMPONENTS ARE NOT INDEPENDENT
Heroku
Discourse
Vine
451kb / 1.7 mb
897kb / 1.88 mb
259kb / 1.1 mb
Travis CI
715kb / 1.0 mb
Digital ocean
402kb / 1.4 mb
The Bad
Performance (ON LOAD)
VENDOR LOCK-IN
God damn state issues
COMPONENTS ARE NOT INDEPENDENT
Ember.computed.FUCK* madness
"3 Ways to enter a page"-issue
Ember-data: Nested API Urls
The Questionable
SIZE (102kb gzipped) W/o Ember-Data (22 kb)
HANDLEBARS/HTMLBARS RESTRICTIONS
TWO-WAY-BINDING BY DEFAULT
NO POJOS but Mixins, classEs & Ember.set
EMBER-DATA
Framework/Complexity/LEarning curve
Conventions :)
When to use
big teams & OOP DUDES
Huge monolithic Single page Apps
DatA heavy apps/Ember-data conform apis
Design heavy apps
Fast prototyping
Enterprise
Apps with A lot of 3rd party libs/Plugins
The (Bright) future
Fastboot (Not ember specific)
NO 2-way-Binding, controllers or Proxies
COMPONENT ROUTING & <ANGLE-BRACKET>
HTMLBARS all the way & Glimmer!!111Elf
Engines
Ember-Cli-Deploy
Ember-Data Stable & JSON-API BY Default
Questions?
Ember.js - The good, the bad, the questionable
By Mario Behrendt
Ember.js - The good, the bad, the questionable
- 3,594