@willgmbr | slides.com/williamgrasel
slides.com/gerardsans | @gerardsans
GMail - 2004
Criado em 2009, aberto em 2010
2014 até out de 2016
Em março de 2017
4.2 Animations
New angular.io website
4.3 HttpClient
Novembro de 2017
PWA support
Melhor suporte a Server Side R.
Menor e mais rápido
Julho de 2018
Último grande lançamento
Entrara em LTS por 3 anos
Em algum momento de 2018
Angular Elements
Simplificação da API de Modulos
Menor, mais rápido e fácil
X . Y . Z
MAJOR MINOR PATCH
schematics/@angular
ng serve --serve-path custom
http://localhost:4200/custom
--missing-translation error
// isso vai detonar o tamanho do seu bundle:
import { Observable } from 'rxjs';
// como era resolvido:
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/interval';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';
Observable.interval(1000)
.filter(x => x%2!==0)
.map(x => x*2)
// nova solução:
import { interval } from 'rxjs/observable/interval';
import { filter, map } from 'rxjs/operators';
// .let para um operador
interval(1000)
.let(filter(x => x%2!==0))
// .pipe para vários seguidos
interval(1000)
.pipe(
filter(x => x%2!==0),
map(x => x*2)
)
// 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]);
<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>
const c = new FormGroup({
one: new FormControl()
}, {updateOn: 'change'});
const c = new FormArray([
new FormControl()
], {updateOn: 'blur'});
const c = new FormControl(, {
updateOn: 'submit'
});
<form>
<!-- Requires formControlName, formControl, ngModel -->
<input type="number" ngModel [min]="2" [max]="100">
</form>
// { 'min': 2, 'actual': 1 }
// { 'max': 100, 'actual': 2000 }
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()));
}
}
// simples
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
registerLocaleData(localeFr);
// completo
import { registerLocaleData } from '@angular/common';
import localeFrCa from '@angular/common/locales/fr-CA';
import localeFrCaExtra from '@angular/common/locales/extra/fr-CA';
registerLocaleData(localeFrCa, localeFrCaExtra);
// valor pre definido:
{{ d | date: 'shortDate' }} // en-US: 1/18/17
// com localização
{{ d | date: 'shortDate': null: 'es' }} // es-ES: 18/1/17
// formato customizado
{{ d | date: 'M/d/yy': null: 'es' }} // es-ES: 18/1/17
// 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'
// 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 $'
// 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 %'
// app.module.ts
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [HttpClientModule], ...
})
export class AppModule {}
// usersService.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable()
export class UsersService {
constructor(private http: HttpClient) { }
public get() {
return this.http.get(USERS_ENDPOINT)
.map(json => json.users);
}
}
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();
}
}
{
"users": [
{
"id": 34,
"username": "spiderman",
"roles": ["admin", "user"],
"superuser": true
},
{
"id": 67,
"username": "batman",
"roles": ["user"]
}
]
}
// 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);
}
}
// 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 });
// 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!');
};
});
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.ts
import {Injectable} from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest }
from '@angular/common/http';
@Injectable()
export class BasicInterceptor implements HttpInterceptor {
intercept(req: HttpRequest, next: HttpHandler): Observable<HttpEvent> {
return next.handle(req);
}
}
// ./auth0.interceptor.ts
@Injectable()
export class Auth0Interceptor implements HttpInterceptor {
intercept(req: HttpRequest, next: HttpHandler): Observable<HttpEvent>{
const clonedRequest = req.clone({
setHeaders: { authorization: localStorage.getItem('auth0IdToken') }
});
return next.handle(clonedRequest);
}
}
// ./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}`);
}
});
}
}
fadeIn
fadeOut
TRANSITIONS
STATE
STATE
fadeIn => fadeOut
fadeOut => fadeIn
fadeIn <=> fadeOut
void
*
void => * :enter
* => void :leave
void <=> *
STATE
STATE
@Component({
template: `
<ul><li *ngFor="let hero of heroes"
[@heroState]="hero.state"
(click)="hero.toggleState()"> {{hero.name}}
</li></ul>
`,
animations: [
trigger('heroState', [
state('inactive', style({
backgroundColor: '#eee',
transform: 'scale(1)'
})),
state('active', style({
backgroundColor: '#cfd8dc',
transform: 'scale(1.1)'
})),
transition('inactive => active', animate('100ms ease-in')),
transition('active => inactive', animate('100ms ease-out'))
])
]
})
@willgmbr