Angular e TypeScript

William Grasel

@willgmbr

Angular quem?

  • Framework MVC, MV* ou MVW
  • Criado em 2009 por Miško Hevery no Google
  • Lançado à público em outubro de 2010, quase ao mesmo tempo que o BackboneJS
  • EmberJS foi lançado em dezembro de 2011
  • ReactJS em março de 2013

Angular em números

Google Apps by Angular

https://www.madewithangular.com/#/categories/google

A web mudou como nunca

  • Web Components
  • Shadow Dom
  • ES6 / ES2015 +
  • Web Workers
  • Service Workers
  • Mobile Apps

Angular 2.0

2014 ~ 2016

TypeScript, WTF??

  • Projeto opensource criado pela Microsoft
  • Mais um transpiler de JS para ES6, 7 e Next
  • Focado em tipos, interfaces e ferramentas assíncronas
  • Onde todas as features são estritamente opcionais
  • Todo JavaScript válido deve ser um TS válido
  • Promete se manter fiel ao futuro do ES Next
  • Foco no ferramental e refactor de grandes apps

Ferramental, Refactor?

Você não precisa tipar seu código

Mas ainda assim pode se beneficiar da tipagem!

Google está sozinho?

Sou obrigado a usar TypeScript?

Arquitetura 2.0

Componentes

  • Base da nova arquitetura do Angular 2+
  • São diretivas que tem uma interface com o usuário através de um template
  • Contem a lógica de interação com usuário
  • São criadas, atualizadas e destruidas pelo angular conforme o usuário interage com a aplicação
  • Contém métodos de rastreio para acompanhar seu ciclo de vida

Exemplo de Componentes


  // TypeScript ou Babel Preset
  import { Component, Input } from '@angular/core';

  @Component({
    selector: 'hero-detail',
    templateUrl: 'app/hero-detail.html'
  })
  export class HeroDetailComponent {
    @Input() hero: Hero;
  }

  // ES5 Puro
  ng.core
  .Component({
    selector: 'hero-detail',
    templateUrl: 'app/hero-detail.html'
    inputs: 'hero'
  })
  .Class({
    constructor: function() {}
  });

Diretivas

  • São objetos de manipulação dinâmica de templates, sem template próprio e sem interação direta com o usuário
  • Vem em dois sabores distintos:
  • Estruturais: que alteram o layout adicionando, removendo ou substituindo porções do template, como *ngIf, *ngFor
  • Atributos: modificam a aparência ou o comportamento de um outro elemento, como o [(ngModel)]

Exemplo de Diretivas


  // TypeScript  
  import { Directive, Input } from '@angular/core';
  import { TemplateRef, ViewContainerRef } from '@angular/core';
  
  @Directive({
    selector: '[myUnless]'
  })
  export class UnlessDirective {
    constructor(
      private templateRef: TemplateRef<any>,
      private viewContainer: ViewContainerRef
    ) {}
    @Input() set myUnless(myUnless: boolean) {
      if (!myUnless)
        this.viewContainer.createEmbeddedView(this.templateRef);
      else this.viewContainer.clear();
    }
  }

  // ES5 Puro
  ng.core
  .Directive({
    selector: '[myUnless]',
    inputs: 'myUnless'
  })
  .Class({
    constructor: [ng.core.TemplateRef, ng.core.ViewContainerRef,
    function(tr, vcr) {
      this.templateRef = tr;
      this.viewContainer = vcr;
    }],
    ngOnChanges: function(changes) {
      if (!changes.myUnless)
        this.viewContainer.createEmbeddedView(this.templateRef);
      else this.viewContainer.clear();
    }
  });

Services

  • É uma categoria ampla como valores, funções, objetos que sejam necessárias para sua aplicação
  • Quase tudo pode ser um serviço, porém cada um deve ter um objetivo e significado único e bem definido
  • Essas são apenas sugestões
  • Angular em si não toma partido sobre como seus services devem ser construídos ou se comportarem

Exemplo de Serviços


  // TypeScript
  import { Injectable } from '@angular/core';
  import { Http, Response } from '@angular/http';

  @Injectable()
  export class CustomerService {
    constructor(private http: Http) { }

    getCustomers() {
      return this.http.get('api/customers')
        .map((response: Response) => response.json());
    }
  }
   
  // ES5 Puro
  ng.core
  .Class({
    constructor: [ng.http.Http, function(http) {
      this.http = http;
    }],
    getCustomers: function(changes) {
      return this.http.get('api/customers')
        .map(function(response) {
          return response.json();
        });
    }
  });

Pipes / Filtros

  • Filtros agora são chamados de Pipes e foram reformulados
  • Pois eram grande causadores de perda de performance
  • Alguns como filter e orderBy não existem mais
  • Pipes são puros por padrão, executando funções puras
  • Somente são executados se a referência mudar
  • Se um filtro puro receber um mesmo objeto duas vezes em ciclos diferentes, o pipe não será executado novamente, mesmo que seus atributos tenham mudado

Exemplo de Pipes


  // TypeScript
  import { Pipe } from '@angular/core';
  import { Http } from '@angular/http';

  @Pipe({ name: 'fetch', pure: false })
  export class FetchPipe {
    private data = null;
    private prevUrl = '';
    constructor(private http: Http) { }
    transform(url: string): any {
      if (url !== this.prevUrl) {
        this.prevUrl = url;
        this.data = null;
        this.http.get(url)
          .map( result => result.json() )
          .subscribe( d => this.data = d );
      }
      return this.data;
    }
  }

  // ES5 Puro
  ng.core
  .Pipe({ name: 'fetch', pure: false })
  .Class({
    data: null, prevUrl: '',
    constructor: [ng.http.Http, function(http) {
      this.http = http;
    }],
    transform: function(url) {
      if (url !== this.prevUrl) {
        this.prevUrl = url;
        this.data = null;
        this.http.get(url)
          .subscribe(function(result){
            this.data = result.json();
          });
      }
      return this.data;
    }
  });

Data Binding


  <div>{{model.name}}</div>

  <div>{{model.name}}</div>

  <model-detail [model]="model"></model-detail>

  <div>{{model.name}}</div>

  <model-detail [model]="model"></model-detail>

  <div (click)="selectItem(model)"></div>

  <div>{{model.name}}</div>

  <model-detail [model]="model"></model-detail>

  <div (click)="selectItem(model)"></div>

  <input [(ngModel)]="model.name">

Two way data binding como sugar sintaxe


  <input
    [value]="model.name"
    (input)="model.name = $event.target.value">

  <input
    [ngModel]="model.name"
    (ngModelChange)="model.name = $event">

  <input [(ngModel)]="model.name">

  [(x)]="e" <==> [x]="e" (xChange)="e=$event"

Detecção de mudanças

  • AngularJS 1.x faz custosos ciclos de dirty checking por toda aplicação a cada mudança nos objetos em seu escopo
  • Angular 2+ cria uma estrutura de grafo em árvore com os componentes, varrendo de forma unidirecional, da raíz para as folhas, propagando as mudanças para evitar novos ciclos
  • Estratégia padrão ainda varre a árvore toda

Grafo em Árvore?

Lembram?

Dados Imutável como a cereja do bolo

  • Angular 2+ sabe tirar proveito de dados imutáveis
  • Podemos mudar a estratégia de atualização dos componentes, ou da aplicação como um todo, para uma mais performática
  • Imutabilidade requer novos paradigmas de programação para se tornar prático e fluente em nosso dia a dia
  • Programação Funcional, Reativa e Streams ao resgate

Streams são a fundação do Angular

  • Streams são fluxos de dados assíncronos
  • Parecidos com promises mas, muito mais poderosos
  • Podem ser mergeados e transformados declarativamente
  • Http e outros módulos que retornavam promises agora retornam streams, retrocompatíveis com promises
  • Reactive Extensions (RxJS) é o novo $q

  // TypeScript
  import { Component, ChangeDetectionStrategy } from '@angular/core';
  import { Observable } from 'rxjs/Rx';

  // TypeScript
  import { Component, ChangeDetectionStrategy } from '@angular/core';
  import { Observable } from 'rxjs/Rx';

  @Component({
    selector: 'hero-message',
    changeDetection: ChangeDetectionStrategy.OnPush,
    template: '<p>Hey: {{ messages | async }}</p>',
  })

  // TypeScript
  import { Component, ChangeDetectionStrategy } from '@angular/core';
  import { Observable } from 'rxjs/Rx';

  @Component({
    selector: 'hero-message',
    changeDetection: ChangeDetectionStrategy.OnPush,
    template: '<p>Hey: {{ messages | async }}</p>',
  })
  export class HeroAsyncMessageComponent {
    private messageList = [
      'You are my hero!',
      'You are the best hero!',
      'Will you be my hero?'
    ];

  // TypeScript
  import { Component, ChangeDetectionStrategy } from '@angular/core';
  import { Observable } from 'rxjs/Rx';

  @Component({
    selector: 'hero-message',
    changeDetection: ChangeDetectionStrategy.OnPush,
    template: '<p>Hey: {{ messages | async }}</p>',
  })
  export class HeroAsyncMessageComponent {
    private messageList = [
      'You are my hero!',
      'You are the best hero!',
      'Will you be my hero?'
    ];
    constructor() {
      this.messages = Observable.interval(500)
        .map(i => this.messageList[i])
        .take(this.messageList.length);
    }
  }

Extensões Reativas Oficiais

  • Extensões oficiais de alta qualidade para tirar o máximo de proveito dos paradigmas funcionais e reativos
  • Vários módulos já disponíveis:

ngrx/store

ngrx/effects

ngrx/db

Muito além da performance

Angular CLI


  > npm install -g angular-cli

  > ng --help

  > npm install -g angular-cli

  > ng --help

  > ng new myProjectName

  > npm install -g angular-cli

  > ng --help

  > ng new myProjectName

  > ng serve

  > npm install -g angular-cli

  > ng --help

  > ng new myProjectName

  > ng serve

  > ng generate route|component|etc myName

  > npm install -g angular-cli

  > ng --help

  > ng new myProjectName

  > ng serve

  > ng generate route|component|etc myName

  > ng build

  > npm install -g angular-cli

  > ng --help

  > ng new myProjectName

  > ng serve

  > ng generate route|component|etc myName

  > ng build

  > ng test

  > ng e2e

Migrando para Angular 2+

  • Migração pode ser feita aos poucos
  • As duas versões podem ser executadas em conjunto
  • É possível utilizar componentes, diretivas, services e filtros do Angular 2+ em uma aplicação AngularJS 1.x e vice versa

Mas como?


  import { UpgradeAdapter } from '@angular/upgrade';

  const adapter = new UpgradeAdapter();
  const app = angular.module('myApp', []);

  adapter.bootstrap(document.body, ['myApp']);


  // Usando Angular2 Componentes numa app Angular1

  app.directive('productDetail',
    adapter.downgradeNg2Component(ProductDetail));


  // Usando Angular1 Componentes numa app Angular2

  const HeroDetail = adapter
    .upgradeNg1Component('heroDetail');

Suporte IE9+

Vamos começar hoje mesmo? \o/

Referências

Perguntas?

Obrigado! =)

Workshop Angular e TypeScript

By Gilson Filho

Workshop Angular e TypeScript

Apresentação para o workshop de Angular e Typescript, baseado nos slides do usuário Willian Grasel (williangrasel).

  • 928