What's coming in Angular 5!
slides.com/gerardsans | @gerardsans
Google Developer Expert
Master of Ceremonies
Blogger
International Speaker
Spoken at 52 events in 18 countries
Angular Trainer
Community Leader
900
1K
Angular In Flip Flops
Timeline
Angular v2
Future ready
Angular v4
Stable and performant
4.2 Animations
New angular.io website
4.3 HttpClient
Angular v5
Smaller, Faster and Easier to use
PWA support
SSR. Material components
Bazel. Build time tools
Semantic Versioning
Semantic Versioning
X . Y . Z
MAJOR MINOR PATCH
Semantic Versioning
- v2 Sept 2016
- v4 March 2017
- v5 Sept/Oct 2017
- v6 March 2018
- v7 Sept/Oct 2018
Long Term Support (LTS)
- Only critical fixes and security patches
- Angular v4 begins October 2017
Goals
- Fixed release calendar
- Incremental upgrades
- Stability
- Reliability
Angular CLI
Rapid Development
Main Features
- Command Line Interface to create Angular Applications
- Full configuration: build, test, scaffolding
- Development and Production
- Follows latest Style Guide
Angular CLI 1.3
- Support for ES8, TypeScript 2.4 and Angular v5
- Smaller bundles
- Scope hoisting (webpack 3)
- --build-optimizer @angular/devkit
- Named Lazy load modules
- --named-chunks
- Server Side Rendering
10th Aug
Angular CLI 1.4
- Schematics
-
schematics/@angular
-
- Custom Serve Path
-
ng serve --serve-path custom
-
http://localhost:4200/custom
-
-
AOT Missing translation Strategy (I18n)
-
--missing-translation error
-
7th Sept
Forms
Latest changes
-
Added new options argument
- FormControl, FormGroup, FormArray
- Fine-tune when to update and validate
- ngFormOptions, ngModelOptions
- New validators
- min and max
New options argument
// validator function
const ctrl = new FormControl('', Validators.required);
// options argument
const form = new FormGroup({
password: new FormControl('')
passwordRepeat: new FormControl('')
}, {
validators: passwordMatchValidator,
asyncValidators: userExistsValidator
});
// arrays
const g = new FormGroup({
one: new FormControl()
}, [passwordMatchValidator]);
Template driven: updateOn
<form [ngFormOptions]="{updateOn: 'blur'}">
<!-- will update on blur-->
<input name="one" ngModel>
</form>
<form [ngFormOptions]="{updateOn: 'blur'}">
<!-- overrides and will update on change-->
<input name="one"
ngModel [ngModelOptions]="{updateOn: 'change', standalone: true}">
</form>
Model driven: updateOn
const c = new FormGroup({
one: new FormControl()
}, {updateOn: 'change'});
const c = new FormArray([
new FormControl()
], {updateOn: 'blur'});
const c = new FormControl(, {
updateOn: 'submit'
});
min and max Validators
<form>
<!-- Requires formControlName, formControl, ngModel -->
<input type="number" ngModel [min]="2" [max]="100">
</form>
// { 'min': 2, 'actual': 1 }
// { 'max': 100, 'actual': 2000 }
Router
Latest changes
- Added new events
- ActivationStart/End
- GuardsCheckStart/End
- ResolveStart/End
- Tracking activation of specific routes
- RouterEvent
New Events
import { Router, ActivationStart, ActivationEnd } from '@angular/router';
import 'rxjs/add/operator/filter';
@Component({
selector: 'my-app',
template: `<router-outlet></router-outlet>`
})
export class AppComponent {
constructor(private router: Router) {
router.events
.filter(e =>
e instanceof ActivationStart || e instanceof ActivationEnd)
.subscribe(e => console.log(e.toString()));
}
}
Tracking specific urls
import { Router, RouterEvent } from '@angular/router';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/distinctUntilChanged';
@Component({
selector: 'my-app',
template: `<router-outlet></router-outlet>`
})
export class AppComponent {
constructor(private router: Router) {
router.events
.filter(e => e instanceof RouterEvent)
.filter(e => e.url == '/about')
.distinctUntilChanged((e1, e2) => e1.id == e2.id && e1.url == e2.url )
.subscribe(e => {
console.log(e.id, e.url);
});
}
}
i18n
Latest changes
- Unicode Common Locale Data Repository (CLDR) instead of Internationalisation API
- Uses timezone/locale from user
- Default locale: en-US
- Support for
- DatePipe, CurrencyPipe, DecimalPipe, PercentPipe
Using specific locales
// no en-US locale
import { registerLocaleData } from '@angular/common';
import localeEs from '@angular/common/locales/es';
registerLocaleData(localeEs);
// period data extras Eg: midnight, noon, morning, afternoon, evening, night
import { registerLocaleData } from '@angular/common';
import localeEs from '@angular/common/locales/es';
import localeEsExtra from '@angular/common/locales/extra/es';
registerLocaleData(localeEs, localeEsExtra);
DatePipe
- New formats
- long, longDate, longTime
- full, fullDate, fullTime
- Added timezone and locale support
DatePipe Examples
// common usage using pre-defined format string
{{ d | date: 'shortDate' }} // en-US: 1/18/17
// specific locale
{{ d | date: 'shortDate': null: 'es' }} // es-ES: 18/1/17
// custom format string
{{ d | date: 'M/d/yy': null: 'es' }} // es-ES: 18/1/17
Breaking changes
DecimalPipe
// number[:digitInfo[:locale]]
// n = 2.718281828459045
// digitInfo defaults to 1.0-3
{{ n | number }} // outputs: '2.718'
{{ n | number: '4.5-5' }} // outputs: '0,002.71828'
{{ n | number: '4.5-5': 'es' }} // outputs: '0.002,71828'
CurrencyPipe
// currency[:currencyCode[:display[:digitInfo[:locale]]]]
// c = 1.3495
// display defaults to symbol
// digitInfo defaults to 1.2-2
{{ c | currency }} // outputs: '$1.35'
{{ c | currency: 'CAD' }} // outputs: 'CA$1.35'
{{ c | currency: 'CAD': 'code' }} // outputs: 'CAD1.35'
{{ c | currency: 'CAD': 'symbol-narrow': '1.2-2': 'es' }} // outputs: '1,35 $'
PercentPipe
// percent[:digitInfo[:locale]]
// p = 0.718281828459045
// digitInfo defaults to 1.0-0
{{ p | percent }} // outputs: '72%'
{{ p | percent: '4.5-5' }} // outputs: '0,071.82818%'
{{ p | percent: '4.5-5': 'es' }} // outputs: '0.071,82818 %'
HttpClient
Main Features
- Immutable Request/Response
- Typed Response body access
- JSON is the default
- Interceptors/Progress Events
- Improvements Unit Testing
Latest changes
- @angular/http is deprecated
- HttpHeaders and HttpParams accept object literals
HttpHeaders and HttpParams
// without classes
http.get(url, { headers: {'X-Option': 'true'} });
http.get(url, { params: {'test': 'true'} });
// using classes
const headers = new HttpHeaders().set('X-Option', 'true');
http.get(url, { headers });
const params = new HttpParams().set('test', 'true');
http.get(url, { params });
Dependencies
- Additional bundle
- @angular/common/http
- Import HttpClientModule
- HttpClient
Creating a Http Service
// app.module.ts
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [HttpClientModule], ...
})
export class AppModule {}
// usersService.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import 'rxjs/add/operator/map';
@Injectable()
export class UsersService {
constructor(private http: HttpClient) { }
public get() {
return this.http.get(USERS_ENDPOINT)
.map(response => response.users);
}
}
JSON Response
{
"users": [
{
"id": 34,
"username": "spiderman",
"roles": ["admin", "user"],
"superuser": true
},
{
"id": 67,
"username": "batman",
"roles": ["user"]
}
]
}
Adding Types
// usersService.ts
interface User {
id: number;
username: string;
roles: Array<string>;
superuser?: boolean;
}
interface UsersResponse {
users: Array<User>;
}
export class UsersService {
public get() {
return this.http.get<UsersResponse>(USERS_ENDPOINT)
.map(response => response.users);
}
}
Consuming a Http Service
import { Component } from '@angular/core';
import { UsersService } from '../services/usersService';
@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(users: UsersService) {
this.userslist = users.get();
}
}
POST
// usersService.ts
@Injectable()
export class UsersService {
constructor(private http: HttpClient) { }
public post() {
const payload = {
userId: 1,
title: 'HttpClient',
body: 'In this article...',
};
return this.http.post('https://jsonplaceholder.typicode.com/posts', payload);
}
}
Upload Progress
// users.service.ts
export class UsersService {
public post(payload) {
return new HttpRequest('POST', URL, payload, {
reportProgress: true,
})
}
}
// users.component.ts
usersService.post(payload).subscribe(event => {
if (event.type === HttpEventType.UploadProgress) {
const percentage = Math.round(event.loaded/event.total*100);
console.log(`${percentage}% uploaded.`);
} else if (event instanceof HttpResponse) {
console.log('Uploaded finished!');
};
});
Interceptors
Main Features
- Transform Request/Response
- Events
- Applications:
- Security
- Logging
- Error Handling
- Caching
Setup
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { BasicInterceptor } from './basic.interceptor.ts';
@NgModule({
providers: [{
multi: true, provide: HTTP_INTERCEPTORS, useClass: BasicInterceptor
}],
})
export class AppModule {}
Basic Interceptor
// ./basic.interceptor.ts
import {Injectable} from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest }
from '@angular/common/http';
@Injectable()
export class BasicInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req);
}
}
Auth0 Interceptor
// ./auth0.interceptor.ts
@Injectable()
export class Auth0Interceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>{
const clonedRequest = req.clone({
setHeaders: { authorization: localStorage.getItem('auth0IdToken') }
});
return next.handle(clonedRequest);
}
}
Logging Interceptor
// ./logging.interceptor.ts
@Injectable()
export class LoggingInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>{
return next.handle(req).do(event => {
if (event instanceof HttpResponse) {
const bodyLength = event.headers.get('Content-Length');
console.log(`Request: ${req.urlWithParams}-Response Size: ${bodyLength}`);
}
});
}
}
Animations
States & Transitions
fadeIn
fadeOut
States & Transitions
TRANSITIONS
STATE
STATE
fadeIn => fadeOut
fadeOut => fadeIn
fadeIn <=> fadeOut
void
*
Special Keywords
void => * :enter
* => void :leave
void <=> *
STATE
STATE
Execution Order
sequence
group
time
Composition
animateChild
time
Stagger
time
Dynamic Selectors
class="container"
class="item"
class="item"
query(selector)
Dynamic Selectors
query('.item')
class="container"
class="item"
class="item"
Dynamic Selectors
query('.item:enter')
class="container"
class="item"
class="item"
class="item"
added
What's coming in Angular 5!
By Gerard Sans
What's coming in Angular 5!
In this interactive session, we'll cover all the cool new stuff and changes in the next version of Angular, which should be released around the time of the conference!
- 5,124