Angular: Passado, Presente e Futuro

William Grasel

@willgmbr | slides.com/williamgrasel

Gerard Sans - GDE

slides.com/gerardsans |  @gerardsans

Timeline

GWT - Google Web Toolkit

GMail - 2004

AngularJS

Criado em 2009, aberto em 2010

Angular v2

2014 até out de 2016

Angular v4

Em março de 2017

4.2 Animations

New angular.io website

4.3 HttpClient

Angular v5

Novembro de 2017

PWA support

Melhor suporte a Server Side R.

Menor e mais rápido

AngularJS 1.7

Julho de 2018

Último grande lançamento

Entrara em LTS por 3 anos

Angular v6 e v7

Em algum momento de 2018

Angular Elements

Simplificação da API de Modulos

Menor, mais rápido e fácil

Semantic Versioning

Semantic Versioning

X . Y . Z

   MAJOR       MINOR        PATCH  

Semantic Versioning

  • v2 Sept 2016
  • v4 March 2017
  • v5 Nov 2017
  • v6 March 2018
  • v7 Sept/Oct 2018

Long Term Support (LTS)

  • Apenas ajustes críticos e paths de segurança
  • v4 se tornou LTS em out 2017

Objetivos

  • Datas de lançamento previsíveis
  • Atualização incremental
  • Estabilidade
  • Confiança

Angular CLI

Rapid Development

Main Features

  • Ferramenta de linha de comando para Angular Apps
  • Full configuration: build, test, scaffolding
  • Development and Production
  • Follows latest Style Guide

Angular CLI 1.3

  • Suporte para ES8, TypeScript 2.4 e Angular v5 beta
  • Bundles menores
    • Scope hoisting (webpack 3)
    • --build-optimizer @angular/devkit
  • Named Lazy load modules
    • --named-chunks
  • Suporte inicial a Server Side Rendering

Angular CLI 1.4

  • Schematics
    • schematics/@angular
  • Custom Serve Path
  • AOT Missing translation Strategy (I18n)
    • --missing-translation error

Angular CLI 1.5

  • Suporte total ao Angular v5.0
  • Compilação incremental
  • AOT por padrão
  • --build-optimizer por padrão
  • Seguindo padrões do TypeScript

Angular CLI 1.6

  • Foco em PWA e SSR
  • Suporte para Service Workers
  • Suporte total a SSR com Angular Universal
  • Geração automática de Application Shell 

Angular CLI 1.7

  • Novo conceito de App Budgets
  • Atualização automática com ng update
  • Suporte ao Angular v6 beta
  • Angular Compiler options
    • fullTemplateTypeCheck
  • Suporte para TypeScript 2.5 e 2.6
  • Suporte para Webpack 4
  • Bundles menores e mais rápidos!

Futuro...

  • Suporte para criar component library 
  • Service Worker e PWA por padrão
  • Bazel e Clojure Compiler

Compiler

Últimas Novidades

  • Foi completamente reescrito na v5
  • Compilação mais rápida
  • Bundle menor
  • Detecção de mudança mais rápida:​​
    • Implementação de Zones melhorada
    • Possibilidade de desabilita-las completamente

Projeto Ivy

  • Novo compiler de componentes para v6
  • Completamente reescrito de novo!
  • Hello World app com apenas 3,2KB (!!!)
  • Já pode ser testado com Angular CLI 1.7

Universal

Últimas Novidades

  • State Transfer API
  • Server Side DOM
  • Melhor integração com 3rd party Component Libraries

RxJS 5.5

Qual era o problema?


// 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)

Pipeable Operators


// 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)
  )

Forms

Últimas Novidades

  • Adicionados novos argumentos para:
    • FormControl, FormGroup, FormArray
  • Ajustes finos para atualização e validação
    • ngFormOptions, ngModelOptions
  • Novos validadores
    • min e 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

Últimas Novidades

  • Novos eventos:
    • ActivationStart/End
    • GuardsCheckStart/End
    • ResolveStart/End

Exemplo

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()));
  }
}
    
 

i18n

Últimas Novidades

  • Unicode Common Locale Data Repository (CLDR) no lugar da Internationalisation API
  • Uso do timezone/locale do usuário
  • Padrão: en-US
  • Suporte para:
    • DatePipe, CurrencyPipe, DecimalPipe, PercentPipe

Adicionando Localização

// 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);

DatePipe

  • Novos formatos:
    • long, longDate, longTime
    • full, fullDate, fullTime 
  • Agora com suporte para localização e timezone

DatePipe Examples

// 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

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

Principais Funcionalidades

  • API simplificada
  • Typed Response body
  • JSON por padrão
  • Interceptors
  • Progress Events
  • Melhoria nos testes de unidade

Últimas Mudanças

  • @angular/http esta depreciado
  • HttpHeaders e HttpParams aceitam object literals

Usando o novo 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 } from '@angular/common/http';

@Injectable()
export class UsersService {
  constructor(private http: HttpClient) { }

  public get() { 
    return this.http.get(USERS_ENDPOINT)
      .map(json => json.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();
  }
}

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);
  }
}

HttpHeaders e 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 });

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

  • Transformar Request / Response:
    • Segurança / Tokens
    • Event 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, next: HttpHandler): Observable<HttpEvent> {
    return next.handle(req);
  }
}

Auth0 Interceptor

// ./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);
  }
}

Event Logging

// ./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

Web Animation API

Que tal levar a mesma API de animação para

todas as plataformas?

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

Exemplo

@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'))
    ])
  ]
})

Perguntas?

Obrigado!

@willgmbr

Angular: Passado, Presente e Futuro

By William Grasel

Angular: Passado, Presente e Futuro

Acabou de lançar a versão 5.0 do Angular, entre a muitas novidades e poucas Breaking Changes, pouca gente entendeu o que isso significa e qual o caminho que a equipe do Google esta tomando, assim como muitas das novidades e features das versões anteriores podem ter passado batidas. Que tal um panorama geral do que esta acontecendo, revisar as mais novas features, e tentar prever um pouco do futuro e o que podemos esperar para os próximos anos?

  • 2,366