Time | Topic |
---|---|
08:30 | Introduction |
08:45 | Setup |
09:00 | Modern Web and Angular 2 Theory |
10:10 | Break 15 min |
10:25 | Work Units I |
12:00 | Lunch Break |
13:00 | Work Units II |
14:50 | Break 10 min |
15:00 | Work Units III |
16:30 | Break 10 min |
17:30 | 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
- Reactive Programming with RxJS
- Change Detection
- 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
# NPM Install for Angular typings
npm install
# This will start a simple server that
# will reload the browser if files change
live-server
NgModule, 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
Module A
Module B
Module C
1
2
3
4
5
6
7
8
// The ng module annotation can be found
// in the core module
import {NgModule} from '@angular/core';
// Other core and supplementing parts of
// angular are also organized in modules
import {BrowserModule} from '@angular/platform-browser';
@NgModule({
// What modules should be imported in the context
// of this module
imports: [BrowserModule],
// Declare any components to be used within this module
declarations: [Comp1, Comp2, Comp3, ...],
// Declare what components should be made available to
// other modules
exports: [Comp1, Comp2, Comp3, ...],
// Specify which components should be bootstrapped
bootstrap: [Component]
})
export class AppModule {}
import {platformBrowserDynamic}
from '@angular/platform-browser-dynamic';
import {AppModule} from './app-module';
platformBrowserDynamic().bootstrapModule(AppModule);
# We are on a branch
git checkout work-unit-1
# We are on a branch
git checkout work-unit-1
Template Syntax: Property and Event Bindings
@Component({
selector: 'counter'
template: '<input type="text" [value]="num">'
})
class AppComponent {
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 AppComponent {
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
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 AppComponent {
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>
<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>'
})
export class App {}
@Pipe({
name: 'reverse'
})
export class ReversePipe {
transform(value) {
return value.reverse();
}
}
app-component.ts
reverse-pipe.ts
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {AppComponent} from './app-component';
import {ReversePipe} from './reverse-pipe';
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent, ReversePipe],
bootstrap: [AppComponent]
})
export class AppModule {
}
app-module.ts
# We are on a branch
git checkout work-unit-2-2
# We are on a branch
git checkout work-unit-2-2
Input and Output Properties
import {Input} from '@angular/core';
@Component({
selector: 'person',
template: '{{details.name}}'
})
export class PersonComponent {
@Input() details;
}
@Component({
selector: 'app',
template: '<person [details]="person"></person>'
})
export class AppComponent {
person = {
name: 'Gion'
};
}
import {Output, EventEmitter} from '@angular/core';
@Component({
selector: 'timer',
template: '<button (click)="startTimer()">Start</button>'
})
export class TimerComponent {
@Output() timeout = new EventEmitter();
startTimer() {
setTimeout(
() => this.timeout.emit('I timed out!'),
5000
);
}
}
@Component({
selector: 'app',
template: '<timer (timeout)="onTimeout($event)"></timer>'
})
export class AppComponent {
onTimeout(message) {
console.log(message);
}
}
# We are on a branch
git checkout work-unit-3
# We are on a branch
git checkout work-unit-3
Content Projection
@Component({
selector: 'container',
template: '<ng-content></ng-content>'
})
export class ContainerComponent {}
@Component({
selector: 'app',
template: '<container><p>Content</p></container>'
})
export class AppComponent {}
Container.ts
App.ts
@Component({
selector: 'container',
template: '<ng-content select="p"></ng-content>'
})
export class ContainerComponent {}
@Component({
selector: 'app',
template: '<container><p>Content</p><container>'
})
export class AppComponent {}
Container.ts
App.ts
# We are on a branch
git checkout work-unit-4
# We are on a branch
git checkout work-unit-4
Querying for Children
import {ViewChild} from '@angular/core';
@Component({
selector: 'app',
template: `
<h1>Hello!</h1>
<my-component></my-component>
`
})
export class AppComponent {
@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 ContainerComponent {
@ContentChild(MyComponent) myComp;
ngAfterContentInit() {
this.myComp.doSomething();
}
}
@Component({
selector: 'app',
template: '<container><my-component></my-component></container>'
})
export class AppComponent {}
# We are on a branch
git checkout work-unit-5
# We are on a branch
git checkout work-unit-5
Dependency Injection
Pre Existing Injectors
Pre Existing Injectors
App Injector
Pre Existing Injectors
App Injector
Component Injectors
Pre Existing Injectors
App Injector
Component Injectors
Element
Injectors
Pre Existing Injectors
App Injector
Component Injectors
Element
Injectors
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();
}
}
import {NgModule} from '@angular/core';
@NgModule({
...
providers: [MyService]
})
export class MyModule {
}
# We are on a branch
git checkout work-unit-6
# We are on a branch
git checkout work-unit-6
Asynchronous operations and Reactive Programming
Item
Error
Completed
import {Observable} from 'rxjs/Rx';
Observable
.from([1, 2, 3])
.map((num) => num * num)
.subscribe((item) => console.log(item));
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {ReactiveFormsModule} from '@angular/forms';
@NgModule({
imports: [BrowserModule, ReactiveFormsModule],
...
})
export class AppModule {}
import {FormControl} from '@angular/forms';
@Component({
selector: 'my-component',
template: `
<input [formControl]="num" type="number">
<output>{{out}}</output>
`
})
export class MyComponent {
out: number;
num: FormControl = new FormControl();
constructor() {
this.num.valueChanges
.subscribe((value) => this.out = value);
}
}
@Component({
selector: 'my-component',
template: `
{{data}}
`
})
export class MyComponent {
asyncSubscription: Subscription;
data: number;
constructor() {
this.asyncSubscription = Observable
.from([1])
.delay(5000)
.subscribe((data) => this.data = data);
}
ngOnDestroy() {
this.asyncSubscription.unsubscribe();
}
}
@Component({
selector: 'my-component',
template: `
{{asyncData | async}}
`
})
export class MyComponent {
asyncData: Observable<number>;
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
Change Detection
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
import {
ChangeDetectionStrategy,
ChangeDetectorRef} from '@angular/core';
@Component({
selector: 'counter',
template: '{{count}}',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class Counter {
count: number = 0;
constructor(@Inject(ChangeDetectorRef) cdr) {
setInterval(() => {
this.count++;
cdr.markForCheck();
}, 1000);
}
}
# We are on a branch
git checkout work-unit-8
# We are on a branch
git checkout work-unit-8