Upload
filippo-zanella-phd
View
1.794
Download
3
Tags:
Embed Size (px)
Citation preview
THE ROAD TO EMBER 2.0FIRST EMBER.JS TREVISO MEETUP
presented by / Filippo Zanella @r4m
HURRY UP!
On July 24th Ember.js will bump to version 2.0. The core team is moving fast (one release per monthly sprint)
Apr 4th v1.11.1, May 13th v1.12, June 12th v1.13.
...BUT DON'T BE SCARED.Version 2.0 marks the transformation of Ember from simply an MVC
framework to a complete front-end stack.
Luckily, Ember 2.0 is not a big-bang rewrite. Staying on thecutting-edge can be done without rewriting your app.
Changes have been rolled out incrementally. The 2.0 release willsimply remove features that have been deprecated.
MOTIVATIONS
STABILITY WITHOUT STAGNATIONIn the greater JavaScript community, getting the latest and
greatest often means rewriting parts of your apps once a year.
The Ember community works hard to introduce new ideas with aneye towards migration. The Ember core-team call this "stability
without stagnation".
When breaking changes are absolutely necessary, they try to makethose changes ones you can apply without too much thought. They
call these "mechanical" refactors.
To further aid in these transitions, a new tab to the EmberInspector that will list all deprecations in your application.
BIG BETSThe first bet was on open standards: JavaScript modules, promises
and Web Components.
The second bet was that the community was tired of hand-rollingtheir own build scripts for each project. They've invested heavily inEmber CLI, giving us a single tool that unifies the community and
provides a venue for disseminating great ideas.
In Ember 2.0, Ember CLI and ES6 modules will become first-classparts of the Ember experience.
That is, you should begin moving your app to Ember CLI now.
LEARNING FROM COMMUNITYEmber core-team have analyzed and discussing React's approach
to data flow and rendering and in particular how they make use of a"virtual DOM" to improve performance.
Ember's view layer is one of the oldest parts of Ember, and was designed for a world where IE7 and IE8 weredominant (gosh!). They've spent the better part of 2014 rethinking the view layer to be more DOM-aware, and the
new codebase (codenamed "HTMLBars") borrows the best ideas from React.
React's "virtual DOM" abstraction also allowed them to simplify theprogramming model of component-based applications.
In Ember 2.0, a "virtual DOM" and data flow model has beenadopted to simplifies communication between components.
SIMPLIFYING EMBER CONCEPTSEmber 2.0 is about simplification, to reduce file sizes, reduce code
complexity, and generally make apps easier to maintain.
The high-level set of improvements that we have planned are:
More intuitive attribute bindingsNew HTML syntax for componentsBlock parameters for componentsMore consistent template scope1-way data binding by default, with opt-in to 2-way bindingsMore explicit communication between componentsRoutes drive components, instead of controller + templateImproved actions that are invoked inside components
NEW FEATURES
INLINE IFIn v1.11 Ember's if helper can be used in the inline form:
{{if isEnabled 'active' 'disabled'}}
EACH WITH INDEXThe each helper will support an index block param in v1.11:{{#each people as |person index|}} {{!-- The first index value will be 0 --}} <div>{{index}}: {{person.name}}</div>{{/each}}
BOUND ATTRIBUTE SYNTAXCurrent Ember developers are familiar with the bind-attr
syntax, used to declare an attribute binding on an HTML element.<a {{bind-attr href=url}}>Click here</a>
Ember 1.11 introduces a more intuitive API for attribute binding.For example, here the color variable is bound to the class of a div:<div class="{{color}}"></div>
The inline if helper can also be used in these contexts:<div class="{{color}} {{if isEnabled 'active' 'disabled'}}"></div>
For some attributes, like the disabled boolean, passing a literalvalue is desirable. An example:
<input disabled={{isDisabled}}>
COMPONENT HELPEREmber components can be bound via the component helper. For
example this logic in a template:{{#if isRed}} {{x-red}}{{else if isBlue}} {{x-blue}}{{else if isGreen}} {{x-green}}{{/if}}
Can be replaced by a property and the component helper.{{component colorComponentName}}
The property colorComponentName should either have a valueof x-red, or x-blue etc. As the value of the computed property
changes, the rendered component will also change.
STORE.UNLOADALL()Previously, store.unloadAll required a modelName argument
to unload records of a type. Now, you can unload all recordswithout calling store.destroy.
@store.unloadAll() can now unload all models when notpassed a model name.
GLIMMEREmber 1.13 is the first release that includes the new Glimmer
rendering engine. Glimmer is:
A new faster rendering engine that is especially fast at updates.An implementation of the React-inspired "just re-render it"programming model for components, with one-way data flow bydefault and better enforcement for data-down, actions-up.Supports ergonomic attributes (<my-link href="{{url}}.html">go home</my-link>), angle-bracketcomponents (<my-component />), that hews closely to HTMLsyntax with a few small enhancements.
THE ATTRS PROPERTYBeginning with Ember 1.13 a component's attributes will be
available in this.attrs rather than on the component itself.
So when a component is invoked this way:{{my-component title=model.name}}
The component will see this.attrs.title as the current valueof model.name. Whenever model.name changes via
observation, or when the parent component is re-rendered, my-component's lifecycle hooks will be triggered, and it will see a new
version of model.name.
THE MUT HELPERWhat if you want to allow the child component to modify the
property explicitly? The mut helper will produce an object thatcontains both a value property and an update method.
In Ember 1.13, you can write:{{my-counter count=(mut activatedCount)}}
Component.extend click: -> this.attrs.count.update(this.attrs.count.value + 1)
The call to {{mut activatedCount}} packages up an objectcontaining both its current value and a callback that allows the
receiving component to modify it.
CLOSURE ACTIONIn Ember 1.x, the actions system used bubbling to pass user
behavior to a parent scope. E.g., when clicking a button an actionmight bubble through several controllers.
Action bubbling was difficult to debug, and plagued by an inabilityto have a return value.
Ember 2.x is component-driven, and replaces action bubblingwith a function-passing solution.
Actions:
Can be passed multiple argumentsReturn a value. E.g. result = this.attrs.submit()Can curry
CLOSURE ACTIONFor example, action submit is passed to my-component where it
is called upon click:app/controllers/index.coffeeimport Ember from 'ember'
Ember.Controller.extend actions: setName: (name) -> model.set('name', name)
{{! app/templates/index.hbs }} {{my-component submit=(action 'setName')}}
app/components/my-component.coffeeimport Ember from 'ember'
Ember.Component.extend click: -> this.attrs.submit(this.get('name'))
NEW EMBER.JS HELPER APIEmber helpers:
Represent a single valueDo not manage DOM or control flowCan recompute themselvesCan optionally access servicesDo not require a dash
app/helpers/full-name.js` `
Ember.Helper.helper (params, hash) -> return params.join(' ')
import Ember from 'ember'
{{full-name "Daniel" model.lastName}}{{my-component name=(full-name model.firstName "Smith")}}{{! The following usage would set the model.name to the new full name when my-component calls the submit action. }}{{my-component submit=(action (mut model.name) (full-name model.firstName "Smith"
DEPRECATIONS
MORE CONSISTENT HANDLEBARS SCOPEIn Ember 1.9, the context-shifting forms of #each and #with have
been deprecated in favor of the named-parameter forms.{{#each post in posts}} {{!-- the context in here is the same as the outside context, and post references the current iteration --}}{{/each}}
{{#each posts}} {{!-- the context in here has shifted to the individual post. the outer context is no longer accessible --}}{{/each}}
{{#with post as otherPost}} {{!-- the context in here is the same as the outside context --}}{{/with}}
{{#with post}} {{!-- the context in here has shifted to the post. the outer context is no longer accessible --}}{{/with}}
MORE CONSISTENT HANDLEBARS SCOPEIn Ember 1.12, the in and as syntax are further deprecated in favor
of block params syntax.
To transition your code to the new syntax, you can changetemplates that look like this:
{{#each people}} <p>{{firstName}} {{lastName}}</p> <p>{{address}}</p>{{/each}}
with:{{#each people itemController="abc" as |person|}} <p>{{person.firstName}} {{person.lastName}}</p> <p>{{person.address}}</p>{{/each}}
OBJECTCONTROLLERExperienced Ember users have enjoyed the use of proxying
behavior in the Ember.ObjectController class since 1.0.
However, this behavior will be removed in Ember 2.0 as theframework migrates to routable components.
OBJECTCONTROLLERTo migrate from an explicitly defined object controller, first convert
the class definition to inherit from Ember.Controller. E.g.:import Ember from "ember";
// Change:export default Ember.ObjectController.extend({// To:export default Ember.Controller.extend({
// ...
Next update any use of {{modelPropertyName}} in templateswith {{model.modelPropertyName}}. You should also review
any computed property dependent keys, observer keys, and getand set statements on the route and controller.
INSTANCES INITIALIZERSBefore FastBoot, you would only ever run applications one at a
time. In FastBoot, it is important for a single node server to be ableto serve a second request while the first one is fetching its data.
In Ember.js 1.12 application boot is separated into two phases:
Application initializers run, to register dependencies andinjections. These initializers are doing work that is shared acrossall FastBoot requests, and therefore should not create instances.Instance initializers run next. This is the right time to do workthat is specific to each FastBoot request. You can createinstances and modify their state here.
ACCESS TO INSTANCES IN INITIALIZERSPreviously, initializers had access to an object that allowed them to
both register new classes and get instances of those classes.
If you have an initializer that gets instances of a class, you need tochange it to use an instance initializer.
The store service is now injected as an . As a consequence, if you had initializers depending onthe store, you should move them to an instance initializer as well, and mark it as after: 'ember-data'.
instanceInitializer
ACCESS TO INSTANCES IN INITIALIZERSChange code that looks like this:
App.initializer name: "clock"
initialize: (container, application) -> application.register("clock:main", Clock) clock = container.lookup("clock:main") clock.setStartTime(Date.now())
To:App.initializer name: "clock"
initialize: (registry, application) -> application.register("clock:main", Clock)
App.instanceInitializer name: "clock"
initialize: (instance) -> clock = instance.container.lookup("clock:main") clock.setStartTime(Date.now())
SHARED GETTER AND SETTEREmber.js 1.12 introduces an improved syntax for computed
properties with a setter. Previously, computed properties with asetter implemented that setter by inspecting the number ofarguments passed to the computed property's descriptor.
For example, this computed property splits a full name into twoparts when set:
fullName: Ember.computed "firstName", "lastName", (key, newName) -> if (arguments.length > 1) parts = newName.split(" ") this.setProperties({ firstName: parts[0], lastName: parts[1] }) return newName else return this.get("firstName") + " " + this.get("lastName")
SHARED GETTER AND SETTERThese uses should be converted to use the new discrete getter and
setter syntax introduced in 1.12:fullName: Ember.computed("firstName", "lastName", { get: function() { return this.get("firstName") + " " + this.get("lastName"); }, set: function(key, newName) { var parts = newName.split(" "); this.setProperties({ firstName: parts[0], lastName: parts[1] }); return newName; }});
EMBER.VIEWEmber 1.x encouraged a Model-View-Controller-Route architecture.Since then, the web has consolidated around a Model-Component-Route pattern for web development that Ember 2.0 also embraces.
Views are removed from the Ember 2.0 API.
YES, VIEWS ARE REMOVED.
However a single release is likely insufficient for large apps toupgrade their entire codebase away from routeable views, and
consequently Ember is providing extended support for views viathe addon.
This addon will remain compatible with Ember until v2.4 of the framework is released.ember-legacy-views
EMBER.VIEWIn most cases Ember views can be replaced with a component. E.g.:
app/templates/show-menu.hbs{{view.title}}
app/views/show-menu.coffee` `
# Usage: {{view "show-menu"}}Ember.View.extend templateName: 'some-menu' title: 'My Menu'
import Ember from 'ember'
Can be replaced with this component:app/templates/components/show-menu.hbs{{title}}
app/components/show-menu.coffee` `
# Usage: {{show-menu}}Ember.Component.extend title: 'My Menu'
import Ember from 'ember'
EMBER.VIEWNote that a component has always its own context in a template.
A view's template may have had access to other variables that werepresent where it was called, such as a controller.
A component template will always be isolated, and if data isneeded it should be passed upon invocation. For example:{{show-menu options=controller.menuOptions}}
EMBER.VIEWROUTABLE VIEWS
Currently, when a template for a given route is rendered, if there isa view with the same name that view will also be used.
For example this view is attached to the rendered route template:app/router.coffee` `
Ember.Router.map -> this.route('about')
import Ember from 'ember'
app/views/about.coffee` `
Ember.View.extend classNameBindings: ['controller.isActive:active']
import Ember from 'ember'
EMBER.VIEWROUTABLE VIEWS
There are only two reasons a view may be used for a route:
To set attribute bindingsTo attach event handlersTo manage all the jquery stuffs
You should migrate away from routed views.
EMBER.VIEWROUTABLE VIEWS
For example to attach bindings to an element in the template issufficient:
app/router.coffee` `
Ember.Router.map -> this.route('about')
import Ember from 'ember'
app/templates/about.hbs<div class="{{if isActive 'active'}}"> <!-- something something --></div>
EMBER.VIEWROUTABLE VIEWS
If attaching events or sharing DOM is necessary, consider acomponent:
app/templates/about.hbs{{#active-layout isActive=isActive}} <!-- something something -->{{/active-layout}}
app/components/active-layout.js` `
Ember.Component.extend classNameBindings: ['isActive:active'] click: -> # Handle click
import Ember from 'ember'
EMBER.VIEWNOTABLE
In preparation for Ember 2.0, 1.13 introduces many deprecations.Some of these include:
All view APIs in Ember (Ember.CoreView, Ember.View,Ember.CollectionView, Ember.ContainerView)Options to the {{#each helper that trigger a legacy and poorlyperforming legacy layer. Like: itemView, itemViewClass,tagName, emptyView and emptyViewClass.The itemController argument for {{#each.The view and viewClass params for {{outlet}}
EMBER.LINKVIEWAs a consequence of the deprecation of Ember.View, many
internal views have been ported to component.
Ember.LinkView, the class that backs the {{link-to}}helper, have been ported to Ember.LinkComponent.
Most uses of Ember.LinkView can be directly ported to theEmber.LinkComponent class.
EMBER.SELECTUsing the Ember.Select global and its view helper form
({{view 'select'}}) is deprecated.
Ember.Select is implemented in a legacy coding style that usespatterns such as observers and two-way data binding that:
can cause pathological performance problemsprovide an experience that is not consistent with idiomaticEmber 2.0 development.
Sadly true. Complex Ember.Select with mixed dynamic content are tricky to be managed.
Legacy support will be provided via the addon.ember-legacy-views
EMBER.SELECTEmber 2.0 provides several new features that make it much more
straightforward to implement </select> tag functionality via thedata-down, actions-up paradigm in one's codebase.
For example, to create a component that displays a prompt and alist of dropdown options, the following code could be used:app/templates/components/my-select.hbs<select {{action 'change' on='change'}}> {{#each content key="@index" as |item|}} <option value="{{item}}" selected={{is-equal item selectedValue}}> {{item}} </option> {{/each}}</select>
EMBER.SELECTapp/components/my-select.js` `
Ember.Component.extend content: [] selectedValue: null
actions: change: -> changeAction = this.get('action') selectedElement = this.$('select')[0] selectedIndex = selectedElement.selectedIndex content = this.get('content') selectedValue = content[selectedIndex]
this.set('selectedValue', selectedValue) changeAction(selectedValue)
import Ember from 'ember'
app/helpers/is-equal.js` `
Ember.Helper.helper ([leftSide, rightSide]) -> return leftSide === rightSide
import Ember from 'ember'
EMBER.SELECTThis component could be used in a template by supplying it an
array of strings as content and an action to call when the usermakes a selection as change:
app/templates/application.hbsThe currently selected item: {{mySelectedItem}}.
{{my-select content=myItems action=(action (mut mySelectedItem))}}
myItems is an array of strings: ['one', 'two', ...]. This uses the action and mut helpers to pass in an action
that will update the (outer scope) mySelectedItem propertywith the user's selection.
TYPEKEY TO MODELNAMEIn Ember Data, when you ask for a model, Ember Data looks its
class up using Ember's API. When the modelclass is looked up, Ember Data stores the type on the model class.
Dependency Injection
For example, when the following code runs in your application:post = this.store.getById('post', 1)
and Ember Data will store the string "post" on the model class:console.log(post.constructor.typeKey) # 'post'
Ember Data uses this typeKey property internally whencreating/extracting payloads in Serializers, and when locating
models in Adapters.
TYPEKEY TO MODELNAMEIn Ember Data 1.0.0-beta.18, the typeKey property is now calledmodelName. In addition, the modelName is a dasherized string(previously it was always normalized to be a camelCased string).
For example, if you had a model called TacoShop, it would be stored on the model's constructor's modelNameproperty as taco-shop, whereas previously it would be stored as tacoShop.
Accessing the typeKey property should still work, but will triggerdeprecation warnings.
IE 8Ember Data v1.0.0-beta.19 is the last release to support InternetExplorer 8. Future versions of Ember Data will not support IE 8.
R.I.P.
RESOURCESThe road to Ember.js 2.0 RFCThe transition to Ember 2.0 in detailDeprecations added in Ember 1.X