Angular with TypeScript
with Andrei Antal
slides.com/andreiantal/angular2_voxxed-25/
slides:
INTRO
About the speaker
Andrei Antal
frontend engineer @ Qualitance
- JavaScript technologies enthusiast
- Community activist
- Occasional speaker
- member of
ngCommunity
Contact me at:
antal.a.andrei@gmail.com
ngBucharest
ABOUT
ANGULAR (2, 4, 5...)
But first a bit of history
2009 - Angular project - Misko Hevery, Adam Abrons
2012 - Angular 1.0 release
- 2-way data binding
- Custom elements (directives)
- Dependency Injection
2013 - React from Facebook
- Virtual DOM
- JSX (HTML + JavaScript)
- Unidirectional data flow
2010 - implemented @Google by Misko Hevery
Announcing Angular 2
2014 - Angular 2 - first announcement
2014 - VueJS - Evan You
2015 - Angular 1.4
2016 - Angular 1.5 - 2 february (components)
2016 - Angular 2 - final release - 14 september
2017 - Angular v4 (just Angular) - 23 March
But first a bit of history
2017 - Angular v5 - 1 November
2016 - Angular 1.6 - 12 december
What's new in Angular
What's new in Angular 2
Features:
- Simplified - modular structure
- Component based architecture (already from Angular 1.5)
- Blazing fast - no more digest cycle*
- New compiler
- New change detection mechanism
- introducing zones
- models as observables
- New module system - ngModule
- New Router
- easier to implement lazy loading
- Dropped bower in favor of npm for package management
What's new in Angular
Features:
- New DI mechanism
- Improved support for server side rendering - AOT compilation
- New Observable based HTTP module
- Supporting modern web standards (shadow DOM)
- New (awesome) animations system
All platforms
All platforms
Community
ES6 AND TYPESCRIPT
JavaScript - ES5
Common problems with Javascript today
- Dynamic typing
- Lack of modularity
- Verbose patterns to achieve some code sanity
ES6/ES2015
- Classes
- Arrow Functions
- Template Strings Inheritance
- Constants and Block Scoped Variables ...spread and ...rest
- Destructuring
- Modules
Classes
class Pizza {
constructor(toppings) {
this.toppings = toppings
}
listToppings() {
toppings.forEach(function(topping) {
console.log(topping.toString();
}
}
}
var pizza = new Pizza();
pizza.listToppings();
Arrow functions
// ES5 function
const sum = function(num1, num2) {
return num1 + num2;
};
// ES6 arrow function
const sum = (num1, num2) => { return num1 + num2 };
// ES5 function
const getTempItem = function(id) {
return {
id: id,
name: "Temp"
};
};
// ES6 arrow function
const getTempItem = id => ({ id: id, name: "Temp" });
- does not bind its own this
- no arguments, super or new.target
Template strings
var firstName = "Andrei",
lastName = "Antal";
// ES5
var fullName = "The full name is: " + firstName + " " + lastName
// ES6 template strings
var fullName = `The full name is: ${firstName} ${lastName}`;
Const and let
// ES5
function() {
if(true) {
var x = 1;
}
console.log(x) // 1
}
// ES6
function() {
if(true) {
let x = 1;
}
console.log(x) // ReferenceError: x doesn't exist here
}
function() {
const a; // error, need to initialize const
const b = 1;
b = 2; // error cannot reasign
}
Destructuring
const animal = {
name: 'Dog',
sound: 'wof'
};
function makeSound(options) {
options.name = options.name || 'animal';
console.log(`The ${options.animal} goes ${options.sound}`)
}
function makeSound({name = 'animal', sound}) {
console.log(`The ${name} goes ${sound}`)
}
makeSound(animal);
let options = {
repeat: true,
save: false
};
let {repeat, save} = options;
console.log(repeat); // true
console.log(save); // false
Others
- Rest and spread
- Modules
- Enhanced Objects
- Iterators
- Generators
- Modules
- Proxies
TypeScript
Static typing
let isDone: boolean = false;
let height: number = 6;
let name: string = "bob";
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
enum Color {Red, Green, Blue};
let c: Color = Color.Green;
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
Classes ++
class Person {
name: string;
nickName?: string;
constructor(name: string) {
...
}
}
const p = new Person("Andrei");
Interfaces
interface ClockInterface {
currentTime: Date;
setTime(d: Date);
}
class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) { }
}
Generics
//declare
private _possessions: Array<Thing>;
constructor(){
//assign
this._possessions = new Array<Thing>();
}
Decorators
@Component({
selector: 'my-app',
styles: ['h1 {color:#545454;}']
template: `
<h1>
Hello from the {{componentName}}.
</h1>
`
})
export class AppComponent {
@Input() componentName = 'AppComponent';
}
.
- Functions that are invoked with a prefixed @ symbol, and immediately followed by a class, parameter, method or property.
BOOTSRAP AND COMPILATION
Bootstrap
- No more ng-app directive for boostrap
- Bootstarp is done explicitly
- There is one Root Module which has one Root Component
- Everything else is a child of the two
- Just In Time (JIT) vs Ahead Of Time (AOT)
Bootstrap
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app';
platformBrowserDynamic().bootstrapModule(AppModule)
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
imports: [ BrowserModule ],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
- Just In Time (JIT) compilation and bootstrap
Bootstrap
// The browser platform without a compiler
import { platformBrowser } from '@angular/platform-browser';
// The app module factory produced by the static offline compiler
import { AppModuleNgFactory } from './app/app.module.ngfactory';
// Launch with the app module factory.
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
- Ahead Of Time (JIT) compilation and bootstrap
index.html
....
<body>
<my-app><!-- content managed by Angular --></my-app>
....
</body>
....
MODULES
MODULES
- Class with @NgModel decorator
- tells Angular how to compile and run module code
-
Built-in modules
- ex: FormsModule, HttpModule, and RouterModule
- Types
- root module
- feature modules
- Different from ES6 Modules - Angular spciffic
MODULES
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { FeatureModule } from '../feature/feature.module';
import { SomeComponent } from './some.component';
import { SomeDirective } from './some.directive';
import { SomePipe } from './some.pipe';
import { SomeService } from './some.service';
@NgModule({
declarations: [SomeComponent, SomeDirective, SomePipe],
imports: [CommonModule, HttpClientModule, FeatureModule]
exports: [SomeComponent],
providers: [ SomeService ],
})
export class SomeModule {}
COMPONENTS
Component architecture
Component architecture
Component
- @Component annotation
- Communication
- Inputs, @Inputs
- Outputs, @Outputs
- Component lifecycle hooks
- Host element interactions
Component
import { Component } from '@angular/core';
import { SomeService } from './some.service';
@Component({
selector: 'app-component',
template: `
<div class="component">
<span> Hello, {{name}} </span>
<button (click)="handleClick()"> Start </button>
</div>
`,
styles: ['span { color: red }'],
})
export class AppComponent {
@Input() name: string;
@Output() onClick: EventEmitter;
private views:number = 0;
constructor(private someService: SomeService) { }
onClick() {
this.views += 1;
this.onClick.emit(this.views);
}
}
Component
import { Component, OnInit } from '@angular/core';
import { SomeService } from './someService';
@Component({
selector: 'parent-component',
template: `
<div class="parent">
<app-component
[name]="guestName"
(onClick)="handleClick($event)"
/>
</div>
<span class="component">some text</span>
`,
styles: ['span { color: blue }'],
})
export class ParentComponent implements OnInit{
private guestName:string;
constructor() { }
ngOnInit() {
this.guestName = "Andrei Antal";
}
}
Component
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { ParentComponent } from './parent.component';
import { SomeService } from './some.service';
@NgModule({
imports: [ BrowserModule ],
declarations: [
ParentComponent,
AppComponent
],
exports: [ ParentComponent ],
providers: [ SomeService ]
})
export class MyModule { }
Lifecycle Hooks
import {Component, OnChanges, OnInit, OnDestroy} from 'angular2/core';
@Component()
export class AppComponent implements OnChanges, OnInit, OnDestroy {
/* 1 */ constructor() { }
// called when an input or output binding changes
/* 2 */ ngOnChanges(changes) { }
// after child initialisation
/* 3 */ ngOnInit() { }
// just before is destroyed
/* 4 */ ngOnDestroy() { }
}
Unidirectional data flow
Component types
TEMPLATING
Templates
import { Component } from '@angular/core';
import { SomeService } from './someService';
@Component({
selector: 'my-component',
template: `
<div>
Hello component
</div>
`,
styles: [`div { color: red }`],
})
export class MyComponent {
@Input() name;
@Output() onClick: EventEmitter;
constructor(private someService: SomeService) {
}
...
}
Templates
<rate-button
[talk]="myTalk"
(rate)="handleRate($event)"
></rate-button>
- Property and event bindings
<input [(ngModel)]="todo.text"></input>
- Two way bindings
<input [ngModel]="todo.text" (ngModelChange)="todo.text=$event">
It's just syntactic sugar for
Templates
<div>Rating {{rating}}</div>
- Interpolation
<talk-cmp
*ngFor="let t of talks; let i=index"
[talk]="t"
[index]="i"
></talk-cmp>
- References
<div [textContent]="interpolate(['Rating'], [rating])"></div>
It's just syntactic sugar for
<confirmation-dialog #dialog></confirmation-dialog>
<button (click)="dialog.open()">Open</button>
- Directives
Template syntax
Syntax | Binding type |
---|---|
<h1>{{title}}</h1> | Interpolation |
<img [alt]="Some image"> | Property |
<li [class.active]="isActive"></li> | Class |
<div [style.width.px]="mySize"> | Style |
<button (click)="onClick($event)"> | Event |
<input [(ngModel)]="data.value"> | Two-way |
DEPENDENCY INJECTION
Dependency Injection
Features
- Framework for resolving dependencies
- Automatic creation and resolution of resources
- The injector - created at bootstrap; configure it by adding providers
- Register providers
- in modules - each service is registered in the root injector => available in the entire application
- in components - a special instance to be used in that component and all children
Services - Injectables
import { Injectable } from '@angular/core';
@Injectable()
export class ExampleService {
someMethod() {
return 'Hey!';
}
}
Creating providers
Dependencies
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { ExampleService } from './example.service';
@NgModule({
imports: [ BrowserModule, FormsModule, HttpClientModule ],
declarations: [ App ],
providers: [ ExampleService ],
bootstrap: [ App ]
})
export class AppModule {}
Registering providers
Dependencies
import { Component } from '@angular/core';
import { ExampleService } from './example.service';
@Component({
selector: 'my-app',
template: '<h1>{{ title }}</h1>',
providers: [ExampleService]
})
export class AppComponent {
title: string;
constructor(private _exampleService: ExampleService) { }
ngOnInit() {
this.title = this._exampleService.someMethod();
}
}
Injecting dependencies
HTTP
HTTP Services
Using Data services
- Singleton instance
- Share state between Components
- Leverage Observables
The HTTP Module
- Primary protocol for client/server communications
- Implements XMLHttpRequest (XHR) and JSONP
- Http methods: GET, POST, PUT, DELETE, PATCH and HEAD
HTTP Client Module
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { ExampleService } from './example.service';
@NgModule({
imports: [ BrowserModule, FormsModule, HttpClientModule ],
declarations: [ App ],
providers: [ ExampleService ],
bootstrap: [ App ]
})
export class AppModule {}
Creating a HTTP Service
// src/users.service.ts
import { Injectable } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
@Injectable()
export class UsersService {
constructor(private http: HttpClientModule) { }
get() {
return this.http.get('/assets/users.json')
}
}
Consuming a HTTP Service
import { Component } from '@angular/core';
import { UsersService } from './users.service';
@Component({
selector: 'users',
template: `<h1>Users</h1>
<tr *ngFor="let user of userslist">
<td>{{user.username}}</td>
</tr>`
})
export class Users {
private userslist;
constructor(private users: UsersService) { }
ngOnInit() {
users.get().subscribe( userResult => this.usersList = userResult );
}
}
Consuming a HTTP Service
import { Component } from '@angular/core';
import { UsersService } from './users.service';
@Component({
selector: 'users',
template: `<h1>Users</h1>
<tr *ngFor="let user of usersList | async">
<td>{{user.username}}</td>
</tr>`
})
export class Users {
private userslist;
constructor(private users: UsersService) {}
ngOnInit() {
this.usersList = this.users.get();
}
}
- Async pipe
RxJS
Streams
Observables
Observables vs Promises
- Observables handle more than one value over time, so we need complete function
myObservable.subscribe(successFn, errorFn)
myPromise.then(successFn, errorFn)
myObservable.subscribe(successFn, errorFn, completeFn)
myPromise.then(successFn, errorFn)
Why observables
- Flexible: sync or async
- Powerful operators
- Less code to write
ROUTER
ROUTER
- Main features
- Url based navigation
- Nested routes
- Navigation guards
- Resolvers
- Easy lazy loading, preloading
Configure routes
import { Routes, RouterModule } from '@angular/router';
import { Home } from './home.component';
import { About } from './about.component';
import { NotFound } from './notfound.component';
const appRoutes: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: Home },
{ path: 'about', component: About },
{ path: '**', component: NotFound }, //always last
];
export const AppRouting = RouterModule.forRoot(appRoutes, { useHash: true });
Configure routes
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { ExampleService } from './example.service';
import { AppRouting } deom './app.routing';
@NgModule({
imports: [
BrowserModule,
FormsModule,
HttpClientModule,
AppRouting
],
declarations: [ App ],
providers: [ ExampleService ],
bootstrap: [ App ]
})
export class AppModule {}
Lazy load routes
import { Routes, RouterModule } from '@angular/router';
import { Home } from './home.component';
import { About } from './about.component';
import { NotFound } from './notfound.component';
const appRoutes: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: Home },
{ path: 'about', component: About },
{
path: 'details',
loadChildren: 'app/details.module#DetailsModule',
},
{ path: '**', component: NotFound }, //always last
];
export const AppRouting = RouterModule.forRoot(appRoutes);
Lazy load routes
import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DetailsComponent } from './details.component';
@NgModule({
imports: [
CommonModule,
RouterModule.forChild([{
path: '',
component: DetailsComponent,
},
{
path: ':id',
component: DetailsComponent,
}]),
],
declarations: [ DetailsComponent ],
exports: [ RouterModule ]
})
export class DetailsModule { }
FORMS
FORMS
Template driven forms
- Two-way data binding
- [(ngModel)]
- Change tracking
- ngForm
- Standard validations
Model driven / Reactive
- Driven programmatically
- FormBuilder, FormGroup, FormControl
- Reactive APIs
- eg - valueChanges
- Composable Validations
Form/Control states
- States
- touched, untouched
- pristine, dirty
- valid, invalid
- Examples
- f.form.valid, email.valid
- CSS classes
- ng-valid, ng-invalid
Form example
@Component({
template: `
<form #f="ngForm" (submit)="onSubmit(f.form.value)">
<input type="email"
[(ngModel)]="model.email"
name="email"
#email="ngModel"
required>
<div [hidden]="email.valid">Email is required</div>
<button type="submit" [disabled]="!f.form.valid">Submit</button>
</form>`
})
export class Contact {
model = {};
onSubmit(value) {
console.log(`Submitted: ${JSON.stringify(value)}`);
}
}
ANGULAR CLI
Angular CLI
- The Angular CLI makes it easy to create an application that already works, right out of the box
// installing
$ npm install -g @angular/cli
// scaffold the project
$ ng new PROJECT_NAME
$ cd PROJECT_NAME
// start the project
$ ng serve
// generate components
$ ng g component my-new-component
THANKS!
bit.ly/2zqBxP7
Repo:
goo.gl/zGbsf5
Instructions:
Angular with TypeScript Workshop
Angular with TypeScript - Voxxed Cluj
By Andrei Antal
Angular with TypeScript - Voxxed Cluj
Angular with TypeScript workshop @Voxxed Days Cluj 2017
- 1,322