AngularJS 2
first impressions by @GionKunz
A Glance Into the Future
Become a contributor!
Close ad
We'll Cover the following
- Core Concepts
- New Syntax
- New Template Syntax
- Directives & Components
- Dependeny Injection
- Writing future proof code
We won't cover
- Router, Forms etc.
- Testing and Mocking
- E2E Testing
- Core Services (XHR etc.)
- Number crunching
THE EVOLUTION OF WEB STANDARDS
THE MODERN WEB
Object.observe()
ECMAScript 6
Web Components
the three main web standards behind AngularJS 2
OBSERVER
Object.observe()
var object = {};
Object.observe(object, function(changeRecords) {
changeRecords.forEach(function(cr) {
console.log(cr.name + ' ' + cr.type);
});
});
object.p1 = 'Property 1';
object.p2 = 'Property 2';
object.p2 = 'Property II';
delete object.p1;
The future
iS
ES6
NOW
ES6 Class
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(this.name + ': Hi there!');
}
static createPerson(name) {
return new Person(name);
}
}
var gion = new Person('Gion');
gion.sayHello();
ES6 Class DESUGARED
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function sayHello() {
console.log(this.name + ': Hi there!');
};
Person.createPerson = function createPerson(name) {
return new Person(name);
};
var gion = new Person('Gion');
gion.sayHello();
ES6 Class Inheritance
class Organism {}
class Person extends Organism {
constructor(name) {
super();
this.name = name;
}
}
class Developer extends Person {
constructor(name, lovesAngular) {
super(name);
this.lovesAngular = lovesAngular;
}
}
var gion = new Developer('Gion', true);
console.log(gion);
ES6 Class Inheritance DESUGARED
let's skip this...
ES6 TEMPLATE STRINGS
var num = 100;
var str = `The square root of ${num} is ${Math.sqrt(num)}`;
console.log(str);
var className = 'my-class';
var title = 'Welcome';
var html = `
<div class="${className}">
<h1>${title}</h1>
</div>
`;
console.log(html);
WITHOUT ES6 TEMPLATE STRINGS
var num = 100;
var str = 'The square root of ' + num + ' is ' + Math.sqrt(num);
console.log(str);
var className = 'my-class';
var title = 'Welcome';
var html = '\n' +
'<div class="' + className + '">\n' +
' <h1>' + title + '</h1>\n' +
'</div>\n';
console.log(html);
Many more super cool features!
Use ES6
TODAY!
WEB COMPONENTS
</>
The Standard
Custom Elements
HTML Imports
Templates
Shadow DOM
Templates
<body>
<template id="template">
<h1>This is a template!</h1>
</template>
</body>
var template = document.querySelector('#template');
var instance = document.importNode(template.content, true);
document.body.appendChild(instance);
Shadow DOM
the video element is shadow dom: http://camendesign.com/code/video_for_everybody/test.html
<body>
<div id="host"></div>
</body>
var host = document.querySelector('#host');
var root = host.createShadowRoot();
var elem = document.createElement('h1');
elem.textContent = 'Bombaclat!';
root.appendChild(elem);
Bored?
One more thing!
ES6 was not enough...
Type annotations
- Availability at runtime
- Framework support (DI)
- Better tooling
- Runtime type assertion
- Can improve code quality
- Code optimization (once standardized)
Meta annotations
- Availability at runtime
- Declarative
- Famework support
- Better tooling (once stabilized)
- Less and more explicit code
- Are proposed to the standard as "decorators"
Robot Drumroll...
Are you ready?
AngularJS 2
Warning!
Under heavy construction
Hello World
Meta Annotations
Template Strings
Type Annotations
@Component({
selector: 'app'
})
@View({
template: `
<div>
<h1>Hello {{ name }}!</h1>
</div>
`
})
class App {
name:string;
constructor() {
this.name = 'World';
}
}
Desugaring TO ES5*
desugaring from AtScript before moving to TypeScript with ES7 like decorators *
function App() {
this.name = 'World';
}
App.annotations = [
new Component({
selector: 'app'
}),
new View({
template:
'<div>\n' +
' <h1>Hello {{ name }}!</h1>\n' +
'</div>\n'
})
];
Architecture
AngularJS 1
View
Controller
Directive
Model
Model
Controller
View
Provider
Value
Factory
Constant
Service
DI
AngularJS 2
Directive
Component
View
Injectable
<<*async>>
<<any>>
Bindings
ELEMENT Property bindings
@Component({
selector: 'counter'
})
@View({
template: '<input type="text" [value]="num">'
})
class App {
num:number = 0;
constructor() {
setInterval(function() {
this.num++;
}.bind(this), 1000);
}
}
[square brackets]
EVENT Bindings
(round brackets)
@Component({
selector: 'app'
})
@View({
template: `
<button (click)="onClick()">Click me</button>
`
})
class App {
onClick() {
alert('Cool!');
}
}
More EVENT Bindings
@Component({
selector: 'app'
})
@View({
template: `
<input id="inp" type="text"
(keyup)="onKeyup($event)" (seven)="onSeven()">
`
})
class App {
onKeyup(event) {
if(event.currentTarget.value.length === 7) {
event.currentTarget.dispatchEvent(new Event('seven'));
}
}
onSeven() {
alert('Cool!');
}
}
To sum it up
(event-binding)="expression"
[property-binding]="expression"
No 2-way bindings
Good old Directives
@Directive({
selector: 'input[pass]',
events: ['passMatch'],
hostListeners: {
keyup: 'onKeyup($event.target.value)'
}
})
class PassInput {
passPhrase:string = 'test123';
passMatch:EventEmitter
= new EventEmitter();
onKeyup(value) {
if(value === this.passPhrase) {
this.passMatch.next('matched');
}
}
}
import PassInput from './pass-input.js';
@Component({
selector: 'app'
})
@View({
template: `
<input type="text"
(pass-match)="onPassMatch($event)">
Matched: {{count}}`,
directives: [PassInput]
})
class App {
constructor() { this.count = 0; }
onPassMatch(event) {
this.count++;
}
}
App Component
Directive
Everything is a Directive!
Everything is a Directive!
Dependency injection
Injection resolution stages
pre-existing injectors
component injectors
element injectors
- The terminal injector throws exception or resolves @Optional to null
- The platform injector resolves browser singleton resources like cookies and location
- Each component has its own injector
- Inherited through DOM order of nested components
- Each element within the shadow DOM of a component has its own injector
- Inherited through DOM order of nested elements
Simple injection
class ListService {
list:Array<string> = ['One', 'Two', 'Three'];
}
@Component({
selector: 'app',
injectables: [ListService]
})
@View({
template: `List: {{ list }}`
})
class App {
constructor(listService:ListService) {
this.list = listService.list;
}
}
Concrete With @Inject
class HelloAbstract {
hello() { throw new Error('Abstract!'); }
}
class HelloConcrete extends HelloAbstract {
hello() { return 'Howdy doodie?'; }
}
@Component({
selector: 'app',
injectables: [
HelloConcrete
]
})
@View({template: '{{ helloService.hello() }}' })
class App {
helloService:HelloAbstract;
constructor(@Inject(HelloConcrete) helloService:HelloAbstract) {
this.helloService = helloService;
}
}
With Injector Binding
class HelloAbstract {
hello() { throw new Error('Abstract!'); }
}
class HelloConcrete extends HelloAbstract {
hello() { return 'Howdy doodie?'; }
}
@Component({
selector: 'app',
injectables: [
bind(HelloAbstract).toClass(HelloConcrete)
]
})
@View({template: '{{ helloService.hello() }}' })
class App {
helloService:HelloAbstract;
constructor(helloService:HelloAbstract) {
this.helloService = helloService;
}
}
Element Level Injection
@Directive({
selector: 'button[kamikaze]'
})
class KamikazeButton {
constructor(elem:NgElement) {
elem.domElement.addEventListener('click', function(event) {
event.currentTarget.style.display = 'none';
}.bind(this));
}
}
@Component({ selector: 'app' })
@View({
template: '<button kamikaze>Remove me</button>',
directives: [KamikazeButton]
})
class App {}
More Element Level Injection
@Directive({
selector: '[color-button]',
hostListeners: {click: 'onClick()'}
})
class ColorButton {
constructor(@Parent() panel:ColorPanel,
@Attribute('color-button') color:string) {
this.color = color;
this.panel = panel;
}
onClick() {
this.panel.bgColor(this.color);
}
}
@Directive({ selector: 'color-panel' })
class ColorPanel {
constructor(@PropertySetter('style.background-color') bgColor:Function) {
this.bgColor = bgColor;
}
}
Future proof Code
Things need to stabilize first
- Still very experimental and things are changing a lot
- Router is not fully ported / documented yet
- No ngResource alternative yet
- Forms module has not matured yet
My Personal Opinion
Not proven yet
- Use ECMAScript 6
- Make everything a directive
- Always use controllerAs syntax
- Avoid $scope.$watch and use reactive approaches
- Only create services
- Use ES6 classes for controllers and services
- Use the new router for 1.4 (angular-new-router)
Thank You!
AngularJS 2 - A Glance Into the Future @ SWISSJS
By Gion Kunz
AngularJS 2 - A Glance Into the Future @ SWISSJS
- 4,426