first impressions by @GionKunz
Object.observe()
ECMAScript 6
Web Components
the three main web standards behind AngularJS 2
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;
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();
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();
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);
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);
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);
Custom Elements
HTML Imports
Templates
Shadow DOM
<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);
<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);
the video element is shadow dom: http://camendesign.com/code/video_for_everybody/test.html
Modules
Classes
Type Annotations
Meta Annotations
Modules
Classes
Type Annotations
Meta Annotations
Modules
Classes
Type Annotations
Meta Annotations
alpha 20
@Component({ selector: 'app' }) @View({ template: ` <div> <h1>Hello {{ name }}!</h1> </div> ` }) class App { name:string; constructor() { this.name = 'World'; } }
Meta Annotations
Template Strings
Type Annotations
class App {} App.annotations = [ new Component({ selector: 'app' }), new View({ template: ` <div> <h1>Hello {{ name }}!</h1> </div> ` }) ];
@Component({ selector: 'app' }) @View({ template: ` <div> <h1>Hello {{ name }}!</h1> </div> ` }) class App {}
Class level meta annotations are instantiated and put into Class.annotations
function App() { this.name = 'World'; } App.annotations = [ new Component({ selector: 'app' }), new View({ template: '<div>\n' + ' <h1>Hello {{ name }}!</h1>\n' + '</div>\n' }) ];
View
Controller
Directive
Model
Model
Controller
View
Provider
Value
Factory
Constant
Service
DI
Directive
Component
View
Decorator
Viewport
<<abstract>>
Directive
Component
View
Injectable
<<*async>>
<<any>>
@Component({ selector: 'counter' }) @View({ template: '<input type="text" [value]="num">' }) class App { num:number = 0; constructor() { setInterval(function() { this.num++; }.bind(this), 1000); } }
@Component({ selector: 'app' }) @View({ template: ` <img [src]="src" [attr.data-custom]="custom"> <input type="number" [class.invalid]="true"> <div [style.background-color]="bgColor">Colored</div> ` }) class App {
src:string = 'http://lorempixel.com/400/200/'; custom:string = 'Random image'; invalid:boolean = true; bgColor:string = '#f00'; }
@Component({ selector: 'app' }) @View({ template: ` <button (click)="onClick()">Click me</button> ` }) class App { onClick() { alert('Cool!'); } }
@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!'); } }
@Component({ selector: 'app' }) @View({ template: ` <input #inp type="text"> <button (click)="onClick(inp.value)">Click Me</button> ` }) class App { onClick(value) { alert(`The value is ${value}`); } }
(event-binding)="expression"
[property-binding]="expression"
#view-reference
@Component({ selector: 'app' }) @View({ template: ` <input #inp type="text" [value]="text" (keyup)="setText(inp.value)"> <button (click)="reverseText()">Reverse</button> ` }) class App { text:string = ''; setText(text) { this.text = text; } reverseText() { this.text = this.text.split('').reverse().join(''); } }
@Directive({ selector: 'input[pass]', properties: { passPhrase: 'pass' }, events: ['passMatch'], hostListeners: { keyup: 'onKeyup($event.target.value)' } }) class PassInput { passPhrase:string; passMatch:EventEmitter = new EventEmitter(); onKeyup(value) { if(value === this.passPhrase) { this.passMatch.next('matched'); } } }
class MatchCounter { count:number = 0; } @Component({ selector: 'app', injectables: [MatchCounter] }) @View({ template: `<input type="text" pass="hello123" (pass-match)="onPassMatch($event)"> Matched: {{matchCounter.count}}`, directives: [PassInput] }) class App { constructor(matchCounter:MatchCounter) { this.matchCounter = matchCounter; } onPassMatch(event) { this.matchCounter.count++; } }
<ul> <li *foo="bar" title="text"></li> </ul>
<ul> <template [foo]="bar"> <li title="text"></li> </template> </ul>
Becomes this
<ul>
<li *for="#item of todoService.items"></li>
</ul>
pre-existing injectors
component injectors
element injectors
pre-existing injectors
component injectors
element injectors
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; } }
class HelloAbstract { hello() { throw new Error('Abstract!'); } } class HelloAmerican extends HelloAbstract { hello() { return 'Howdy doodie?'; } } @Component({ selector: 'app', injectables: [ HelloAmerican ] }) @View({template: '{{ helloService.hello() }}' }) class App { helloService:HelloAbstract; constructor(helloService:HelloAmerican) { this.helloService = helloService; } }
class HelloAbstract { hello() { throw new Error('Abstract!'); } } class HelloAmerican extends HelloAbstract { hello() { return 'Howdy doodie?'; } } @Component({ selector: 'app', injectables: [ HelloAmerican ] }) @View({template: '{{ helloService.hello() }}' }) class App { helloService:HelloAbstract; constructor(@Inject(HelloAmerican) helloService:HelloAbstract) { this.helloService = helloService; } }
class HelloAbstract { hello() { throw new Error('Abstract!'); } } class HelloAmerican extends HelloAbstract { hello() { return 'Howdy doodie?'; } } @Component({ selector: 'app', injectables: [ bind(HelloAbstract).toClass(HelloAmerican) ] }) @View({template: '{{ helloService.hello() }}' }) class App { helloService:HelloAbstract; constructor(helloService:HelloAbstract) { this.helloService = helloService; } }
class Store { values:Array<number> = [1, 2, 3, 4]; static asyncFactory() { return new Promise(function(resolve) { setTimeout(function() { resolve(new Store()); }, 5000); }); } } @Component({ selector: 'app', injectables: [ bind(Store).toAsyncFactory(Store.asyncFactory) ] }) @View({ template: 'Values: {{ values }}' }) class App { constructor(@InjectPromise(Store) promise:Promise<Store>) { promise.then(function(valueStore) { this.values = valueStore.values; }.bind(this)); } }
@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 {}
@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; } }
Text