Time | Topic |
---|---|
09:00 | Introduction |
09:15 | Setup |
09:30 | Modern Web and Angular 2 Theory |
10:40 | Break 15 min |
10:55 | Work Units I |
12:05 | Lunch Break |
13:00 | Work Units II |
14:50 | Break 10 min |
15:00 | Work Units III |
16:30 | Break 10 min |
16:40 | TODO Application Walkthrough |
17:40 | Use Angular 2 Today |
18:00 | Finish |
- ES6, ES7 and TypeScript
- Web Components Basics
- SystemJS and JSPM
- Components, templates and bootstrapping
- Property and Event Bindings
- View Variables, Pipes and Template Elements
- Input and Output properties
- Content Projection
- Querying Child Directives
- Dependency Injection
- Asynchronous and Reactive Programming
- Change Detection
- NgUpgrade
- Directives
- Router
- Testing
You're all set to go :-)
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);
export var someVariable = 10;
export function someFunction() {};
export default class SomeClass {};
Module A
Module B
// Multiple imports
import {someVariable, someFunction} from 'A';
// Import alias
import {someVariable as myLocalVariable} from 'A';
// Wildcard import
import * as A from 'A';
// Import the default export
import SomeClass from 'A';
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);
var square = x => x * x;
var multiply = (a, b) => a * b;
console.log(square(10));
console.log(multiply(3, 7));
var Counter = {
count: 0,
start() {
setInterval(() => this.count++, 1000)
}
};
var Counter = {
count: 0,
start: function() {
setInterval(function() {
this.count++;
}.bind(this), 1000);
}
};
class Person {
static sex = {
female: 1,
male: 2,
unknown: 3
};
name = 'no-name';
sex = Person.sex.unknown;
}
function LogAccess(obj, prop, descriptor) {
const delegate = descriptor.value;
descriptor.value = function() {
console.log(`${prop} was called!`);
return delegate.apply(this, arguments);
};
}
class MoneySafe {
@LogAccess
openSafe() {
this.open = true;
}
}
const safe = new MoneySafe();
safe.openSafe(); // openSafe was called!
function add(a: number, b: number): number {
return a + b;
}
console.log(add(10, 4));
class ListWrapper<T> {
list: Array<T> = [];
add(item: T) {
this.list.push(item);
}
get(index: number): T {
return this.list[index];
}
}
var listWrapper: ListWrapper<string> = new ListWrapper();
listWrapper.add('Hello');
console.log(listWrapper.get(0));
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 bread and butter for modern front-ends
<head>
<title>My First Project</title>
<script src="jspm_packages/system.js"></script>
</head>
<body>
<script>
System.config({
transpiler: "typescript"
})
System.import('./app.ts');
</script>
</body>
export class HelloWorld {
static sayHello() {
console.log('Hello World!');
}
}
HelloWorld.sayHello();
# Clone project with git
git clone https://github.com/oddeven/angular-2-workshop.git
# Change into directory
cd angular-2-workshop
# This will start a simple server that
# will reload the browser if files change
live-server
Component, template and bootstrap
// The component annotation can be found
// in the core module
import {Component} from '@angular/core';
@Component({
// A CSS like selector that will tell
// Angular where to attach our component
selector: 'my-component',
// The HTML template that is going to be
// used as view
template: '<p>Hello World!</p>'
})
export class MyComponent {}
@Component({
selector: 'my-component'
template: '<p>Hello World!</p>'
})
<body>
<my-component></my-component>
</body>
+
=
<body>
<my-component>
<p>Hello World!</p>
</my-component>
</body>
<my-component>
</my-component>
Host Element
import {bootstrap} from '@angular/platform-browser-dynamic';
import {MyComponent} from './my-component';
bootstrap(MyComponent);
# We are on a branch
git checkout work-unit-1
# We are on a branch
git checkout work-unit-1-prepared
Template Syntax: Property and Event Bindings
@Component({
selector: 'counter'
template: '<input type="text" [value]="num">'
})
class App {
num: number = 0;
constructor() {
setInterval(() => this.num++, 1000);
}
}
<p [title]="title"></p>
Regular DOM property
<p [attr.role]="role"></p>
Attribute binding (setAttribute)
<p [class.is-active]="isActive()"></p>
CSS class binding (classList)
<p [style.display]="!isActive() ? 'none' : null"></p>
CSS style binding
@Component({
selector: 'app'
template: `
<button (click)="onClick()">Click me</button>
`
})
class App {
onClick() {
alert('Cool!');
}
}
<input (input)="handleInput($event.target.value)">
# We are on a branch
git checkout work-unit-2-1
# We are on a branch
git checkout work-unit-2-1-prepared
Template Syntax: Local View Variables, Pipes and
Template Elements
@Component({
selector: 'app'
template: `
<input #inp type="text">
<button (click)="onClick(inp.value)">Click Me</button>
`
})
class App {
onClick(value) {
alert(`The value is ${value}`);
}
}
<ul>
<li *ngFor="let item of items">
{{item}}
</li>
</ul>
<ul>
<li *ngFor="let item of items; let i = index">
{{item}} at index {{i}}
</li>
</ul>
<ul>
<li *ngFor="let item of items; let i = index">
{{item}} at index {{i}}
</li>
</ul>
<ul>
<template ngFor #item #i="index" [ngForOf]="items">
<li>{{item}} at index {{i}}</li>
</template>
</ul>
is de-sugaring to:
<p *ngIf="isActive()"></p>
<div [ngSwitch]="value">
<p *ngSwitchWhen="1">One</p>
<p *ngSwitchWhen="2">Two</p>
<p *ngSwitchDefault>Other</p>
</div>
NgIf
NgSwitch
@Component({
selector: 'app',
template: '<p>{{data | reverse}}</p>',
pipes: [ReversePipe]
})
export class App {}
@Pipe({
name: 'reverse'
})
export class ReversePipe {
transform(value) {
return value.reverse();
}
}
App.ts
ReversePipe.ts
# We are on a branch
git checkout work-unit-2-2
# We are on a branch
git checkout work-unit-2-2-prepared
Input and Output Properties
import {Input} from '@angular/core';
@Component({
selector: 'person',
template: '{{details.name}}'
})
export class Person {
@Input() details;
}
@Component({
selector: 'app',
template: '<person [details]="person"></person>',
directives: [Person]
})
export class App {
person = {
name: 'Gion'
};
}
import {Output, EventEmitter} from '@angular/core';
@Component({
selector: 'explosive',
template: '<button (click)="onClick()"></button>'
})
export class Explosive{
@Output() boom = new EventEmitter();
onClick() {
this.boom.next('I exploded!');
}
}
@Component({
selector: 'app',
template: '<explosive (boom)="onBoom($event)"></explosive>',
directives: [Explosive]
})
export class App {
onBoom(message) {
console.log(message);
}
}
# We are on a branch
git checkout work-unit-3
# We are on a branch
git checkout work-unit-3-prepared
Content Projection
@Component({
selector: 'container',
template: '<ng-content></ng-content>'
})
export class Container {}
@Component({
selector: 'app',
template: '<container><p>Content</p></container>',
directives: [Container]
})
export class App {}
Container.ts
App.ts
@Component({
selector: 'container',
template: '<ng-content select="p"></ng-content>'
})
export class Container {}
@Component({
selector: 'app',
template: '<container><p>Content</p><container>',
directives: [Container]
})
export class App {}
Container.ts
App.ts
# We are on a branch
git checkout work-unit-4
# We are on a branch
git checkout work-unit-4-prepared
Querying for Children
import {ViewChild} from '@angular/core';
@Component({
selector: 'app',
template: `
<h1>Hello!</h1>
<my-component></my-component>
`,
directives: [MyComponent]
})
export class App {
@ViewChild(MyComponent) myComp;
ngAfterViewInit() {
this.myComp.doSomething();
}
}
@ViewChildren(MyComponent) myComp: QueryList<MyComponent>;
Query multiple children using a live list of view children
@ViewChild('myComp') myComp: MyComponent;
Query for a component using its view reference
<my-component #myComp></my-component>
@ViewChild('myInput') myInput: ElementRef;
Query for a DOM element using its view reference
<input type="text" #myInput>
import {ContentChild} from '@angular/core';
@Component({
selector: 'container',
template: '<ng-content></ng-content>'
})
export class Container {
@ContentChild(MyComponent) myComp;
ngAfterContentInit() {
this.myComp.doSomething();
}
}
@Component({
selector: 'app',
template: '<container><my-component></my-component></container>',
directives: [MyComponent]
})
export class App {}
# We are on a branch
git checkout work-unit-5
# We are on a branch
git checkout work-unit-5-prepared
Dependency Injection
import {Injectable} from '@angular/core';
@Injectable()
export class MyService {}
import {Inject} from '@angular/core';
@Component({
selector: 'my-component',
template: '<p>{{data}}</p>',
providers: [MyService]
})
export class MyComponent {
constructor(@Inject(MyService) myService) {
this.data = myService.getData();
}
}
# We are on a branch
git checkout work-unit-6
# We are on a branch
git checkout work-unit-6-prepared
Asynchronous operations and Reactive Programming
import {Observable} from 'rxjs/Rx';
Observable
.from([1, 2, 3])
.map((num) => num * num)
.subscribe((item) => console.log(item));
import {Control} from '@angular/common';
@Component({
selector: 'my-component',
template: `
<input [ngFormControl]="num" type="number">
<output>{{out}}</output>
`
})
export class MyComponent {
out: number;
num: Control = new Control();
constructor() {
this.num.valueChanges
.subscribe((value) => this.out = value);
}
}
@Component({
selector: 'my-component',
template: `
{{data}}
`
})
export class MyComponent {
constructor() {
this.asyncData = Rx.Observable
.from([1])
.delay(5000);
this.asyncSubscription =
this.asyncData.subscribe((data) => this.data = data);
}
ngOnDestroy() {
this.asyncSubscription.unsubscribe();
}
}
@Component({
selector: 'my-component',
template: `
{{asyncData | async}}
`
})
export class MyComponent {
constructor() {
this.asyncData = Rx.Observable
.from([1])
.delay(5000);
}
}
# We are on a branch
git checkout work-unit-7
# We are on a branch
git checkout work-unit-7-prepared
Change Detection
import {
ChangeDetectionStrategy,
ChangeDetectorRef} from '@angular/core';
@Component({
selector: 'counter',
template: '{{count}}',
changeDetection: ChangeDetectionStrategy.Detached
})
export class Counter {
count: number = 0;
constructor(@Inject(ChangeDetectorRef) cdr) {
setInterval(() => {
this.count++;
cdr.markForCheck();
}, 1000);
}
}
var array = [1, 2, 3];
// We update an element but the
// reference does not change
array[0] = 2;
var array = [1, 2, 3];
// We use slice to create a copy
// of the array first
array = array.slice();
array[0] = 2;
Adding item by mutating array
Adding item immutable
# We are on a branch
git checkout work-unit-8
# We are on a branch
git checkout work-unit-8-prepared
# Clone project with git
git clone https://github.com/oddeven/angular-2-todo.git
# Change into directory
cd angular-2-todo
# If you're using NVM
nvm use
# Install NPM dependencies (jspm)
npm install
# Install JSPM dependencies
jspm install
# This will start a simple server that
# will reload the browser if files change
live-server
:-)
Preparation
Migration
Angular 1
Angular 2
Preparing your application for the change to Angular 2 by following best practices.
Component structuring, ES6 / TypeScript, only use Services, no $scope.$apply, reactive approach etc.
Include Angular 2 within application and start migrating piece by piece using NgUpgrade.