We start our coverage of ng-conf 2015 with day 2's keynote by Misko Hevery, because that's what everyone had been waiting for since the debut of 2.x at his keynote back at ng-conf Europe. This time, Misko laid out the rationale for the platform rewrite quite clearly, explaining that it was driven by developer productivity needs and performance.
It's about speed
Everyone who has worked with Angular knows that it gets slower the more expressions and watches you have on a page. That is becuase of a number of factors, including the template syntax, the role of the digest cycle, and two-way databinding.
Angular 2.0 addresses this in a few key ways:
- Two-way data-binding is gone – because of the resolution that must go on when data bindings are assigned in such a way, performance takes a dive. We aren't exactly sure how two-way bindings are going to be replaced yet, but between Misko and the day 1 keynote it was made clear just how much faster Angular performs without it.
- Scopes (and true controllers) are gone – the scope concept was helpful for new developers, but actually could be simplified.
- The template syntax has been changed (and for the better).
Let's review these points in detail.
Angular 2 removes $scope
and controller
Scopes are redundant in Angular 2. Since 1.1, you can begin to assign variables to the controller itself, naming it along the way. Every time you create a controller, it is called as a constructor function. So all along they were maintaining both the constructor instance (anything you assign to this
) as well as a scope chain with prototypical inheritance.
This became messy – anyone who has had to debug issues with nested scopes can attest to the potential of losing things because they are nested at the wrong level of detail.
In Angular 2, they remove the concept of a controller, and make every object a component. So the main difference:
// angular 1.x:
angular.module('foo')
.controller('DemoCtrl', function($scope) {
$scope.action = function() {
};
$scope.property = 'foo';
});
// angular 2.x:
var component = function() {
this.action = function() {
};
this.property = 'foo';
};
New syntax – friend or foe (answer: friend)
Misko addressed the syntax controversy in GitHub Issue 139 by explaining exactly why it needed to be changed – because there were too many variants of the microsyntax. For example, an IDE or syntax checker could have a number of potential decisions to make in templates:
<button ng-click="clickMe()">click me</button>
<ng-repeat="item in items">...</ng-repeat>
<div ng-show="{{ a === b ? true : false }}"</div>
In each of the cases above, you would have to know the internals of the actual directive to make a decision about what argument types it accepts. The arguments could be a static string value, an expression (with or without {{ }}), or a method call in the scope. In the end, though, there are only two options – is it an expression or a statement?
In the new Angular template syntax, we see the following VALID HTML decorations on the HTML properties to the left:
<button (click)="md.goGetEmTiger()">Go!</button>
<div [show]="md.isEnabled">stuff</div>
Do you see the difference? Executable method directive properties are surrounded with parentheses, which tells Angular to execute the expression. Properties are surrounded with brackets, as in the named property accessor.
Oh, and what is that md? Well, if you learned the controllerAs
syntax in 1.x, you know you can rename the controller to a variable name in the expression. So, in Angular 1.x, you do this:
<div ng-controller="ContentCtrl as md">
...
</div>
// angular 1.x controllers:
.controller('ContentCtrl', function() {
this.goGetEmTiger = function() {...};
this.isEnabled = true;
});
Now in Angular 2, it gets more componentized. It’s not as easy to show you the before/after here without some explaining some of the Angular 2 component architecture. So for that, we will look at something a little more concrete – a list of customers. But before we start, let’s define an Angular 2 Component:
A component in Angular 2 is an object decorated with two annotations (or the ES 5 equivalent, which I have not yet seen).
@Component
selector
– a CSS-style query selector to attach the component to in the template-
componentServices
– any other component in the system – how we now import and/or assign dependencies
@Template
url
– Defines the template (alternate properties can specify a function or URL) that can be applied to evaluate the contentdirectives
– Defines any imported directives
Of the two annotations, the @Template
one is a bit more interesting. Because Angular 1.x didn't know what components (nee directives) you were using, it had to scan a large list of all imported directives in the module. Now, with true modularization at the component level, you declare which directives your template uses right in the Javascript, which makes error checking much easier, and performance greater due to less items to match against.
Here is a ficticious example Angular 2 component (note, I may get some of this wrong, but until I have my own working example it will do). I'm using ES6 and AtScript syntax (somewhat abridged), which can be easily transpiled down to ES 5 using Google's Traceur:
import {Component, Template} from 'lib/annotations';
import {{Foreach}} from 'lib/angular2';
import {customerPersister} from 'app/persisters/customer';
...
@Component({
selector: 'customers',
componentServices: [
customerPersister
]})
@Template({
url: './customers.html',
directives: [Foreach]
})
class CustomerManager {
customers: [],
currentCustomer;
constructor(existingCustomers) {
this.customers = existingCustomers || [];
}
addCustomer(customer) {
this.customers.push(customer);
}
...
}
Now, the template – note how we find the component using a tag that matches our selector
in the @Component
annotation:
<customers>
<h2>Customers</h2>
<ul>
<li *foreach='#customer in customers'>
{{ customer.firstName }}
{{ customer.lastName }}
</li>
</ul>
<h2>Add a customer...</h2>
<input [value]="customer.firstName">
<input [value]="customer.lastName">
<button (click)="save(customer)">Save</button>
</customers>
Components can be broken up even further than this. For example, I see two components here that could share a common service for persistence and component access – a list component and an editing component. More about this in a future blog post.
There is much more to say about this topic, and the template syntax will be tweaked where necessary. But what this does is provide IDE tools (even Sublime) much better ability to provide itellisense-type syntax checking and error detection.
The Shadow DOM
To improve the performance of Angular in modern browsers, they've introduced the Shadow DOM see the spec for sleep reading. As Angular makes changes to the components, the changes are reflected in this shadow of the original DOM. Then, at the appropriate time for rendering takes place, a diff is taken between the visible, (light) dom, and the shadow is projected onto it by doing only the delta of the changes. This vastly improves DOM update performance.
There was a hilarious talk by Shai Reznik called Angular Wat? – here is the moment where he calls out the term "Transclusion" – a joyous much-watch. Humor aside, it turns out that the shadow dom has all of the features of transclusion and more, so that the Angular team can safely retire that term and use the Shadow DOM in Angular 2. Non-modern browsers will be given a shim to emulate the feature.
Templates can now pass data by reference
Angular now pass data into expressions directly by reference. This means that nothing gets stringified unless you want it to, and it makes it possible to bind an object to a component. From his talk, he discussed mounting a standard YouTube Google Web Component and treating it like a variable on the page (#v):
<button (click)="v.play()">Play</button>
<google-youtube #v
[videoid]="id"
[head]="{start: s, end: e}"
(state) ="onState($event)">
...
We snuck in the concept of the WebComponent standard here. On modern (read: Chrome?) browsers, the google-youtube
tag, properly configured, will be able to communicate with Angular templates. The #v
attribute on the google-youtube
tag exposes the web component as v
, which we see being used by the button above.
The data passed between Angular and the page, because of the special ()
and []
properties, can be pre-scanned and dealt with using real object references, unlike Angular 1.x which treated all of them as strings, which had to be evaluated in a complex parsing operation first. Now that we're dealing with standard Javascript objects, the parsing becomes more basic, and we can really pass objects back and forth between components on the page as [properties]
.
Wrap-up and resources
I’ve lifted and modified a few examples from the slides, as well as from the TypeScript 1.5 AngularJS TodoMVC demo.
This is the first in a series of articles I'll be publishing on the ng-conf 2015
conference. There is a lot more about Angular 2, including the benchpress tool, how it integrates with the new router, and more. To get the full run-down on the 2.0 architectural decisions, you can watch Misko's keynote below.