Skip to content

Latest commit

 

History

History
2156 lines (1576 loc) · 52.8 KB

angular.md

File metadata and controls

2156 lines (1576 loc) · 52.8 KB

Angular

Trabalhando com angular-cli.

Angular é uma plataforma de aplicações web de código-fonte aberto e front-end baseado em TypeScript liderado pela Equipe Angular do Google e por uma comunidade de indivíduos e corporações. Angular é uma reescrita completa do AngularJS, feito pela mesma equipe que o construiu. --Wiki


Anexos


Preparando o ambiente de desenvolvimento com Angular cli

Angular cli reference

Pre-requisitos:

  • NodeJS instalado
sudo npm i -g @angular/cli
sudo npm i -g typescript

Iniciar um novo projeto com o Angular cli

ng new app-name

Entre no diretório do projeto para começar a trabalhar nele:

cd app-name

Rodando a aplicação

O comando abaixo irá fazer o build da aplicação e rodar no http://localhost:4200/, você poderá acompanhar pelo browser o efeito das alterações do projeto.

ng serve

ou abreviado:

ng s

Componentes

Angular 2 é orientado a componentes, isso significa que você vai escrever diversos componentes minúsculos que juntos constituirão uma aplicação inteira. Um Component é a combinação de um template HTML com uma classe que controla parte da tela. --Matera

É possível criar os componentes manualmente ou de forma mais simplificada, utilizando o angular-cli.

Os arquivos de componentes estão em: src >>> app

Criar componente utilizando o angular cli

Basta digitar no terminal:

ng g c nome-do-componente

ou se já existir o diretório:

ng g c diretorio/nome-do-componente

onde:

  • g: gerar
  • c: component

Esse comando irá criar a pasta do component e os arquivos. Neste caso, ele também irá criar o arquivo html onde você deverá editar o conteúdo do seu component. É uma forma diferente da demonstrada no modo manual com template string, ambas as formas podem ser utizadas (template string, html separado).

No arquivo nome-do-componente.component.ts, gerado na criação do componente, haverá o seletor que você poderá usar no HTML, por exemplo:

No arquivo btn.component.ts:

import { Component, OnInit } from "@angular/core";

@Component({
  selector: "app-btn", // este seletor
  templateUrl: "./btn.component.html",
  styleUrls: ["./btn.component.sass"]
})
export class BtnComponent implements OnInit {
  constructor() {}

  ngOnInit() {}
}

O seletor app-btn, poderá ser usado no html como:

<app-btn></app-btn>

!Nota: se o component estiver importado no app.module.ts que é o module raíz do projeto, você também poderá utilizar o component dentro dos arquivos html de outros components.

!Convenção: Criar nome das pastas e dos arquivos de componentes em Kebab Case (com letra minúscula e palavras separadas por "-")

!Convenção: classes são escritas em Pascal Case (todas as primeiras letras em maiúsculo)

ng Content

Projeção de conteúdo:

<app-example>
  <ng-content></ng-content>
</app-example>

Templates

html = template

!Boa prática: no Component -> Utilizar template string somente se tiver até 3 linhas. Mais do que isso é recomendado um arquivo HTML a parte.

Interpolação e Diretivas

Interpolação

Podemos usar a interpolação para atribuir valores em um componente.

{{ x }}

Por exemplo:

no arquivo exemplo.component.ts

import { Component, OnInit } from "@angular/core";

@Component({
  selector: "app-exemplo",
  templateUrl: "./exemplo.component.html",
  styleUrls: ["./exemplo.component.sass"]
})
export class ExemploComponent implements OnInit {
  myName: string;

  constructor() {
    this.myName = "Lays";
  }

  ngOnInit() {}
}

Depois de ter feito todos as configurações necessárias no arquivo de modules.

Podemos utilizar a interpolação no arquivo exemplo.component.html

<h2>Olá, meu nome é {{ myName }}!</h2>

Inserindo o componente para exibição, o resultado será:

Olá, meu nome é Lays


Modules

Em Angular, um módulo é um mecanismo para agrupar componentes, diretivas, canais e serviços relacionados, de forma que possam ser combinados com outros módulos para criar uma aplicação. Uma aplicação Angular pode ser vista como um quebra-cabeça onde cada peça (ou cada módulo) é necessária para poder ver a imagem completa.

Outra analogia para entender os módulos angulares são as classes. Em uma classe, podemos definir métodos públicos ou privados. Os métodos públicos são a API que outras partes do nosso código podem usar para interagir com ela, enquanto os métodos privados são detalhes de implementação ocultos. Da mesma forma, um módulo pode exportar ou ocultar componentes, diretivas, tubulações e serviços. Os elementos exportados devem ser usados ​​por outros módulos, enquanto os que não são exportados (ocultos) são usados ​​apenas dentro do próprio módulo e não podem ser acessados ​​diretamente por outros módulos de nosso aplicativo. --Angular 2 training book

O angular-cli cria automaticamente um arquivo app.modules.ts:

(esse é o módulo raiz do projeto)

import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { AppRoutingModule } from "./app-routing.module";

// importar components
// boa prática: agrupar os imports de components após pular uma linha dos imports iniciais
import { AppComponent } from "./app.component";
import { MeuPrimeiroComponent } from "./meu-primeiro/meu-primeiro.component";
import { Componente2Component } from "./componente2/componente2.component";

// declaratios, imports são metadados
@NgModule({
  // declarations: componentes, diretivas e pipes
  declarations: [AppComponent, MeuPrimeiroComponent, Componente2Component],
  // import: outros módulos para serem utilizados dentro deste módulo ou de algum componente que pertence a este módulo
  imports: [BrowserModule, AppRoutingModule],
  // providers: serviçoes disponíveis para os componentes, e nesse caso, na aplicação global, já que AppModule é global:
  providers: [],
  // instanciado no carregamento da SPA:
  bootstrap: [AppComponent]
})
export class AppModule {}

Criar módulo

(esse é um módulo de funcionalidade do projeto)

Na pasta do projeto, digite no terminal:

ng g m nome-do-modulo

Ex:

ng g m my-module

irá criar um diretório com o nome do módulo (ex: my-module), com o arquivo .ts correspondente ao módulo criado:

import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";

@NgModule({
  declarations: [],
  imports: [CommonModule]
})
export class MyModuleModule {}

Adicionar componentes ao módulo

ver como criar componentes

O angular-cli cuida dos imports dos componentes que são criados e das declarações, acrescentando-os no app.module.ts.

Se você tiver a extensão Auto Import, ela irá acrescentar os imports necessários dos componentes no arquivo de módulo, porém é necessário acrescentar o trecho de export para indicar o que deve ser efetivamente exportado e exibido.

,
  exports: [
    NomeDoComponent
  ]

Assim, nosso arquivo de exemplo fica assim:

import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";

import { BtnComponent } from "./btn.component";

@NgModule({
  declarations: [BtnComponent],
  imports: [CommonModule],
  exports: [BtnComponent]
})
export class BtnModule {}

!Dica: Importar módulo -> Sempre que estiver trabalhando com mais módulo além do módulo app.module.ts, será necessário exportar este módulo em seu arquivo e importar no arquivo raíz.

Depois disso será necessário importar o módulo de funcionalidade dentro do módulo raíz (app.module.ts).

Exemplo: no arquivo app.module.ts:

Importar a classe:

import { BtnModule } from "./btn/btn.module";

Em seguida, importar o módulo no NgModule:

@NgModule({
  declarations: [
    AppComponent,
    MeuPrimeiroComponent,
    Componente2Component
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BtnModule // importar o módulo aqui
  ],
  providers: [],
  bootstrap: [AppComponent]
})

Também é possível fazer uso de componentes privados, não incluindo-os no imports.

!Nota: se o component estiver importado no app.module.ts que é o module raíz do projeto, você também poderá utilizar o component dentro dos arquivos html de outros components.


Services e Injeção de Dependência

O serviço é simplesmente uma função javascript, juntamente com suas propriedades e métodos associados, que podem ser incluídos (via injeção de dependência) nos componentes do Angular 2. Eles permitem desenvolver código para tarefas específicas que podem ser usadas nesses componentes. --Coursetro Angular distingue componentes de serviços para aumentar a modularidade e a reutilização. Ao separar a funcionalidade relacionada à visualização de um componente de outros tipos de processamento, você pode tornar suas classes de componentes simples e eficientes. --Angular.io

Para criar na raiz do projeto (dir app):

ng g s service-name

Para criar em um diretório de componente já existente:

ng g s dir-name/service-name

Este comando irá criar dois arquivos:

  • service-name.service.spec.ts
  • service-name.service.ts

!Boas práticas fazer o uso de services para injetar dados, ao invés de fazer direto por diretiva.

Utilizando o mesmo exemplo das diretivas:

Seguimos 3 passos:

  1. no arquivo numbers.service.ts:
import { Injectable } from "@angular/core";

// @injectable é um decorator
@Injectable({
  providedIn: "root"
})
export class NumbersService {
  constructor() {}

  getNumbers() {
    // adiciona um return com os valores a serem injetados:
    return ["1", "2", "3"];
  }
}
  1. no arquivo numbers.component.ts:
import { Component, OnInit } from "@angular/core";

// importar a classe NumbersService
import { NumbersService } from "./numbers.service";

@Component({
  selector: "app-numbers",
  templateUrl: "./numbers.component.html",
  styleUrls: ["./numbers.component.sass"]
})
export class NumbersComponent implements OnInit {
  // retirar o array daqui e inserir no arquivo de service:
  // numbers: string[] = ['1', '2', '3'];
  numbers: string[];

  // Instanciar via construtor (pode ser private ou public)
  constructor(private numbersService: NumbersService) {
    this.numbers = this.numbersService.getNumbers();
  }

  ngOnInit() {}
}
  1. no arquivo numbers.component.html:
<ul>
  <li *ngFor="let num of nums">{{ num }}</li>
</ul>

Por fim, O resultado será:

  • 1
  • 2
  • 3

Data binding

Associação de informações que estão no componente para o template e vice-e-versa.

componente <----info----> template

componente ---> template

  • interpolação: {{x}}
  • property binding: [propriedade]='x'

template ---> component:

  • evento: (event)='handler'

componente <---> template:

  • Two-way data binding: [(ngModel)='property']

Two-way data binding: a sincronização entre o model e a view. Quando os dados no model são alterados, a view reflete a alteração e, quando os dados da view mudam, o model também é atualizado. --w3schools

componente ---> template

Exemplos: Dado o arquivo: example.components.ts

import { Component, OnInit } from "@angular/core";

@Component({
  selector: "app-example",
  templateUrl: "./example.component.html",
  styleUrls: ["./example.component.css"]
})
export class DataBindingComponent implements OnInit {
  url: string = "https://github.com/layshidani/my-learning-notes/";
  aprender: boolean = true;
  urlImagem: string = "http://lorempixel.com/400/200/animals/";

  getValor() {
    return "Hello";
  }

  praticar() {
    return true;
  }

  constructor() {}

  ngOnInit() {}
}
  • interpolação: {{x}} example.component.html:
<section>
  <h3>Interpolation</h3>
  <p>string rendereizada com interpolação: {{ url }}</p>
  <p>Interpolação com expressões: 1 + 1 = {{ 1 + 1 }}</p>
  <p>Interpolação com getValor: {{ getValor() }}</p>
  <p>Com expressão bool: {{ aprender && praticar() }}</p>
  <img src="{{urlImagem}}" />
</section>

O resultado do código acima será: Resultado interpolação

  • property binding: [propriedade]='x' Considerando o mesmo arquivo example.components.ts...

No html:

<section>
  <h3>Property Binding</h3>
  <p>Property-biding (<code>[src]="urlImagem"</code>):</p>
  <img [src]="urlImagem" />
  <!-- Os métodos são equivalentes -->
  <p>Que é o mesmo que (<code>bind-src="urlImagem"</code>):</p>
  <img bind-src="urlImagem" />
</section>

O resultado do código acima será: Resultado Property Binding

!Quando não houver uma property no elemento para ser utilizado no property-binding (como o src da img), pode-se utilizar como property:

[attr.colspan]="valor"

Class Binding

Adicione e remova nomes de classe CSS do atributo de classe de um elemento com uma vinculação de classe. Class Binding se parece com Property Biding, mas em vez de uma propriedade de elemento entre colchetes, começa com a classe de prefixo, opcionalmente seguida por um ponto (.) Eo nome de uma classe CSS: [class.class-name]. --Angular guide

Exemplo:

<section>
  <article>
    <h3>Selecione uma classe:</h3>
    <!-- #var = variável local do template para que seja possível acessar esse select l (template reference variable) -->
    <select #classe (change)="0">
      <option value="alert-success">Sucesso</option>
      <option value="alert-danger">Erro</option>
    </select>
  </article>

  <article>
    <!-- [class.className]="expression" -->
    <div
      class="alert"
      role="alert"
      [class.alert-success]="classe.value == 'alert-success'"
    >
      A simple primary alert—check it out!
    </div>
    <div
      class="alert"
      role="alert"
      [class.alert-danger]="classe.value == 'alert-danger'"
    >
      A simple primary alert—check it out!
    </div>
  </article>
</section>

Alguns exemplos retirados do Guia:

<h3>Substituir todas as class:</h3>
<div class="item clearance special" [attr.class]="resetClasses">
  Reset all classes at once
</div>
<h3>Add a class:</h3>
<div class="item clearance special" [class.item-clearance]="itemClearance">
  Add another class
</div>
<h3>toggle the "special" class on/off with a property:</h3>
<div [class.special]="isSpecial">The class binding is special.</div>

<h3>binding to class.special overrides the class attribute:</h3>
<div class="special" [class.special]="!isSpecial">
  This one is not so special.
</div>

<h3>Using the bind- syntax:</h3>
<div bind-class.special="isSpecial">This class binding is special too.</div>

Style Binding

A sintaxe do Style binding se parece com a de Property Binding. Em vez de uma propriedade de elemento entre colchetes, comece com o estilo de prefixo, seguido por um ponto (.) E o nome de uma propriedade de estilo CSS: [style.style-property]. --Guia

Alguns exemplos:

<button [style.color]="error ? 'red': 'green'">Red</button>
<div
  class="alert alert-danger"
  role="alert"
  [style.display]="classe.value == 'alert-danger' ? 'block' : 'none'"
>
  Cuidado ERRO Selecionado
</div>

Event Binding

MDN - lista de eventos

Alguns exemplos de evento são:

  • (click)="myFunction()"
  • (submit)="myFunction()"
  • (blur)="myFunction()"
  • (focus)="myFunction()"
  • (scroll)="myFunction()"
  • (keyup)="myFunction()"
  • (keypress)="myFunction()"
  • (keydown)="myFunction()"
  • (input)="myFunction()"
<tag (target event name)="templateStatment()">Text</tag>

exemplo:

<button (click)="onSave($event)">Save</button>

Outro exemplo: no arquivo event-example.component.html:

<h2
  (mouseover)="onMouseOverOut()"
  (mouseout)="onMouseOverOut()"
  [class.highlight]="isMouseOver"
>
  Passe o mouse sobre este texto :)
</h2>

no arquivo event-example.component.ts:

import { Component, OnInit } from "@angular/core";

@Component({
  selector: "app-event-example",
  templateUrl: "./event-example.component.html",
  styles: [
    `
      .highlight {
        background-color: green;
        font-weight: bold;
      }
    `
  ]
})
export class EventExampleComponent implements OnInit {
  isMouseOver: boolean = false;

  onMouseOverOut() {
    this.isMouseOver = !this.isMouseOver;
  }
  constructor() {}

  ngOnInit() {}
}

no arquivo app.component.html:

<app-data-binding></app-data-binding>

Two-way Data Binding

Propriedade + evento

[()]="value"

!Dica: Quando trabalhando com formulários, deverá importar o @angular/forms no arquivo de module.ts:

// ...
// importa o módulo
import { FormsModule } from '@angular/forms'

@NgModule({
 // ...
  imports: [
    //...
    // add no imports
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
// ...

Exemplo:

Supondo um objeto chamado pet, que está no arquivo component.ts:

pet = {
  name: "Dexter",
  age: 2
};

No component.html teremos:

<section>
  <h3>Two-way Data Binding</h3>
  <input type="text" [(ngModel)]="'Nome: ' + pet.name" />
  <input type="text" [(ngModel)]="'Idade: ' + pet.age" />
  <h4>Resultado:</h4>
  <p>
    Pet: Meu nome é {{ pet.name }} e tenho {{ pet.age }} ano(s) :)
  </p>
</section>

Input/Output Properties - Comunicação entre componentes

IT Next - input/output tutorial.

Utilizar dados de um componente em outro.

Considere um componente pai e um componente filho:

  • Input: de pai para filho (de fora para dentro)
  • Output: de filho para pai (de dentro para fora)

Input

de pai para filho (de fora para dentro)

Por padrão, as propriedades de um componente só estão disponíveis para ele mesmo, se quisermos expo-las para outro componente, devemos utilizar o input.

No arquivo filho.component.ts importamos os dados do componente pai para que possam ser utilizados pelo componente filho:

// não esquecer de importar a classe Input do @angular/core
import { Component, OnInit, Input } from '@angular/core';

// ...

@Input() originalName: type;

// ou podemos usar um nome de variável diferente
@Input('originalName') alias: type;

no componente pai (pai.component.html) ao utilizarmos o componente filho através de sua tag, devemos 'disponibilizar/repassar' a variável correspondente ao dado através dos properties:

<app-filho [variable]="variable"></app-filho>

<!-- ou para uso de hardCoded, valor cravado, não precisa de [] -->
<app-filho variable="10"></app-filho>

<!-- ou em caso de nome diferente -->
<app-filho [alias]="originalName"></app-filho>

Exemplo:

pai.component.ts:

import { Component } from "@angular/core";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  title = "input";

  esposa: string;

  constructor() {
    this.esposa = "Juliane";
  }
}

pai.component.html

<h1>Angular @Input()</h1>
<h2>Pai</h2>
<h3>Esposa do pai (* Valor original do pai):</h3>
<p>{{ esposa }}</p>

<hr />

<!-- Aqui, estamos passando utilizando um alias (esposaDoPai) para passar ao filho o valor de esposa que vem do pai -->
<app-filho [esposaDoPai]="esposa"></app-filho>

filho.component.ts

// import do Input no @angular/core
import { Component, OnInit, Input } from "@angular/core";

@Component({
  selector: "app-filho",
  templateUrl: "./filho.component.html",
  styleUrls: ["./filho.component.css"]
})
export class FilhoComponent implements OnInit {
  // Pega o Input do pai (esposaDoPai) e utiliza no filho como mãe
  @Input("esposaDoPai") mae;

  constructor() {}

  ngOnInit() {}
}

filho.component.html

<h2>Filho</h2>
<h3>Mãe do filho (Input vindo do pai):</h3>
<p>{{ mae }}</p>

Resultado: Exemplo Input

Output


TODO escrever sobre:

  • Output
  • EventEmitter()
  • ViewChild()
  • ContentChild()

de filho para pai (de dentro para fora)

// importar a classe Output do @angular/core
import { Component, OnInit, Output } from '@angular/core';

// utilizar o decorator @Output
@Output() originalName: type

// ou podemos usar um nome de variável diferente
@Output('alias') originalName: type

Local reference

Pode ser utilizado em qualquer elemento HTML.

<input #localReferenceName />

Exemplo:

<!-- guardamos a referência local do select em #num -->
<!-- na mudança de opção ele passa o valor para a função  selectedValue -->
<select #num (change)="selectedValue(num)">
  <option value="1">1</option>
  <option value="2">2</option>
</select>

<!-- mostramos o valor selecionado com interpolação -->
<p>O valor selecionado é {{ selectedNum }}</p>
// ...
export class AppComponent {
  // declaramos um valor inicial/padrão do selectedNum,porque ele sempre vai começar com o primeiro option pré selecionado
  selectedNum = "1";

  // agora passamos para a função selectedValue o num que será do tipo HTMLInputElement
  selectedValue(num: HTMLInputElement) {
    // mudamos o valor do selectedNum pelo valor selecionado com num.value
    this.selectedNum = num.value;
  }
  // ...
}

Alguns Comandos angular-cli

Documentação

Build

ng build
  • default dev : sem minificação
  • --prod: minificado

irá gerar o folder dist com os arquivos do build.

!dica: lib npm http-server para rodar a aplicação.

Verificar lint

ng lint

Teste unitário

ng test

Teste end-to-end com Protractor

ng e2e

Modificar estilo de um projeto existente

!Modifica apenas os próximos componentes, os já existentes continuarão com as extensões selecionadas anteriormente. Para modificar, será necessário mudar as extensões manualmente nos arquivos.

ng set defaults.styleExt <estilo>

Estilo:

  • scss para sass
  • less para less
  • styl para stylus

Diretivas

Angular 2 categoriza diretivas em 3 partes:

  1. Diretivas com modelos conhecidos como Componentes
  2. Diretivas que criam e destroem elementos DOM conhecidos como Diretivas Estruturais
  3. Diretivas que manipulam o DOM alterando o comportamento e a aparência conhecidas como Diretivas de Atributo --codementor.io

Diretivas Estruturais

Diretivas estruturais são responsáveis pelo layout HTML. Eles moldam ou reformulam a estrutura do DOM, geralmente adicionando, removendo ou manipulando elementos. --Angular Guide

ngFor

*ngFor="expression"

Exemplo: Considere um array de números declarados no arquivo example.component.ts, arr = [1, 2, 3];:

<ul>
  <li *ngFor="let num of arr">{{ num }}</li></li>
</ul>

Neste caso, será gerado um <li> para cada número do array.

Resultado:

  • 1
  • 2
  • 3

ngIf

*ngIf="expression"

Exemplo: Considere este select de example.component.html:

<select #num (change)="selectedValue(num)">
  <option value="1">1</option>
  <option value="2">2</option>
  <option value="3">3</option>
</select>

<p *ngIf="selectedNum <= '2'">O valor selecionado é {{ selectedNum }}</p>

Neste caso, será feita a validação da expressão: selectedNum <= '2' e a tag <p> só será exibida caso a validação seja positiva (1 e 2).

ngElse

TODO

ngSwitch

O ngSwitch funciona como o switch que utilizamos no js comum.

Só para relembrarmos como é o switch no js:

switch (expression) {
  case x:
    // code block
    break;
  case y:
    // code block
    break;
  default:
  // code block
}

Exemplo:

ao clicarmos nos botões abaixo queremos exibir uma mensagem de acordo com o botão

<!-- ao clicarmos atribuimos o valor a variavel msg -->
<button type="button" class="btn btn-primary" (click)="msg = 'warning'">
  ok
</button>
<button type="button" class="btn btn-success" (click)="msg = 'success'">
  Success
</button>
<button type="button" class="btn btn-danger" (click)="msg = 'danger'">
  Danger
</button>
<button type="button" class="btn btn-warning" (click)="msg = 'warning'">
  Warning
</button>

<!-- aqui estão as mensagens. Fazemos a verificação: -->
<!-- [ngSwitch]="msg"  -->
<div class="container" [ngSwitch]="msg">
  <!-- ngSwitchDefault: msg padrão -->
  <p *ngSwitchDefault>Clique em um botão</p>
  <!-- caso a msg = ok exibimos esse parágrafo -->
  <p *ngSwitchCase="'ok'">OK! :D</p>
  <!-- caso a msg = success exibimos esse parágrafo, etc -->
  <p *ngSwitchCase="'success'">Sucesso! :)</p>
  <p *ngSwitchCase="'danger'">Perigo! :z</p>
  <p *ngSwitchCase="'warning'">Atenção! 8/</p>
</div>

Diretivas de atributo

ngClass

É usado para adicionar e remover classes CSS em um elemento HTML. Podemos vincular várias classes CSS ao NgClass simultaneamente, que podem ser adicionadas ou removidas. Existem diferentes maneiras de vincular classes CSS a NgClass que estão usando string, array e objeto. --Concrete Page

Aplica uma classe CSS. Ao utilizar '-' deve-se estar entre aspas simples (ex: 'background-color') ou utilizar CamelCase (ex: backgroundColor)

[ngClass]="{'classe-css': expression}"

<!-- varias -->
[ngClass]="{ 'classe-css': expression, 'classe-css': expression, 'classe-css':
expression }"

Exemplo:

<ul *ngFor="let fruit of fruits">
  <li
    [ngClass]="{
    'text-red': fruit.name === 'apple',
    'text-yellow': fruit.name === 'banana',
    'text-orange': fruit.name === 'orange'
  }"
  >
    {{ fruit.name }}
  </li>
</ul>

Sintaxe alternativa para ngClass

[class.prop]="value"

ngStyle

Aplica uma propriedade CSS. Ao utilizar '-' deve-se estar entre aspas simples (ex: 'background-color') ou utilizar CamelCase (ex: backgroundColor)

[ngStyle]="{propCSS: expression}"

<!-- em casos que se usam unidades: -->
[ngStyle]={'propCSS.unit': value}

Exemplos:

<!-- simples, atribui a cor azul ao background-color deste elemento -->
<p [ngStyle]="{ backgroundColor: 'blue' }">{{ person.age }}</p>

<!-- com expressão -->
<p [ngStyle]="{ backgroundColor: person.age > 18 ? 'green' : 'yellow' }"></p>

<!-- com unidade em -->

<p [ngStyle]="{ 'fontSize.em': 2.5}">{{ person.age }}}</p>

Sintaxe alternativa ngStyle

[style.<property
  >]=""

  <!-- em casos que se usam unidades: -->

  [style.<property>.<unit>]=""</unit></property></property
>

Exemplo:

[style.color]="green"

<!-- para aplicar uma font-size de 16px -->
[style.font-size.px]="16"

Diretivas customizadas

TODO

ng g d dir/directive-name

geralmente criamos no dir shared.

Será gerado um arquivo directive-name.directive.ts

// importar o ElementRef e o Renderer
import { Directive, ElementRef, Renderer } from "@angular/core";

@Directive({
  // este nome do seletor deverá ser utilizado na tag html que receberá a diretiva
  selector: "[appDiretivaExample]"

  // para restringir a tag a que esse diretiva poder ser aplicada, basta adicionar o nome da tag 'nome-da-tag[nomeDiretiva]', inclusive para tag componentes
  // selector: 'button[appDiretivaExample]'
})
export class DiretivaExampleDirective {
  // geralmente utilizamos a inicial _ na nomeação para indicar que é uma variável privada
  constructor(private _elementRef: ElementRef, private _renderer: Renderer) {
    // aplicamos as modificações
    this._renderer.setElementStyle(
      this._elementRef.nativeElement,
      "background-color",
      "red"
    );

    // este console.log mostra os atributos que podem ser modificados
    // console.log(this._elementRef);

    // Boas práticas: o uso do ElementRef, para modificação direta do DOM, não é recomendado por questoes de vulnerabilidade da aplicação. Assim, é recomendado utilizar o Renderer
    // this._elementRef.nativeElement.style.backgroundColor = 'green';
  }
}

para aplicar a diretiva customizada na tag:

<h1>Diretiva Customizada</h1>
<button appDiretivaExample>Exemplo</button>

HostListener

Permite ouvir eventos no elemento ou componente hospedeiro (host).

Neste exemplo, mudamos o tamanho da fonte quando passamos o mouse sobre o texto

// importa HostListener
import { Directive, ElementRef, Renderer, HostListener } from "@angular/core";

@Directive({
  // este nome do seletor deverá ser utilizado na tag html que receberá a diretiva
  selector: "[appHighlightMouse]"
})
export class HighlightMouseDirective {
  // @HostListener('nomedoevento') função() {}
  @HostListener("mouseenter") onMouseEnter() {
    this._renderer.setElementStyle(
      this._elementRef.nativeElement,
      "font-size",
      "2em"
    );
  }

  @HostListener("mouseleave") onMouseLeave() {
    this._renderer.setElementStyle(
      this._elementRef.nativeElement,
      "font-size",
      "1em"
    );
  }

  constructor(private _elementRef: ElementRef, private _renderer: Renderer) {}
}

HostBinding

Permite definir propriedades no elemento ou componente hospedeiro (host) da diretiva por meio de uma variável.

Este exemplo faz o mesmo que o demonstrado em HostListener, porém de uma maneira otimizada utilizando o HostBinding.

HostingListener + HostBinding:

// importa HostBinding
import {
  Directive,
  ElementRef,
  Renderer,
  HostListener,
  HostBinding
} from "@angular/core";

@Directive({
  selector: "[appHighlightMouse]"
})
export class HighlightMouseDirective {
  @HostListener("mouseenter") onMouseEnter() {
    // utilizando HostBinding
    this.changeSize = "2em";

    // método anterior
    // this._renderer.setElementStyle(
    //   this._elementRef.nativeElement,
    //   'font-size',
    //   '2em'
    // )
  }

  @HostListener("mouseleave") onMouseLeave() {
    // utilizando HostBinding
    this.changeSize = "1em";

    // método anterior
    // this._renderer.setElementStyle(
    //   this._elementRef.nativeElement,
    //   'font-size',
    //   '1em'
    // )
  }

  // @HostBinding('style.cssAtributeName') varName: type;
  @HostBinding("style.fontSize") changeSize: string;

  constructor() // private _elementRef: ElementRef,
  // private _renderer: Renderer
  {}
}

Operador Elvis (?)

Exemplo:

<!-- elvis -->
<h1>{{ person?.name }}</h1>

<!-- é o mesmo que  -->
<h1>{{ person != null ? person.name : '' }}</h1>

Model

TODO


Services

Service é uma categoria abrangente que inclui qualquer valor, função ou recurso de que um aplicativo precisa. Um serviço é tipicamente uma classe com um propósito estreito e bem definido. Deve fazer algo específico e fazê-lo bem.

Angular distingue componentes de serviços para aumentar a modularidade e a reutilização.

Ao separar a funcionalidade relacionada à visualização de um componente de outros tipos de processamento, você pode tornar suas classes de componentes simples e eficientes.

Idealmente, o trabalho de um componente é permitir a experiência do usuário e nada mais. Um componente deve apresentar propriedades e métodos para vinculação de dados, a fim de mediar entre a visualização (renderizada pelo modelo) e a lógica do aplicativo (que geralmente inclui alguma noção de um modelo).

Um componente pode delegar determinadas tarefas aos serviços, como buscar dados do servidor, validar a entrada do usuário ou registrar-se diretamente no console. Ao definir essas tarefas de processamento em uma classe de serviço injetável, você torna essas tarefas disponíveis para qualquer componente. Você também pode tornar seu aplicativo mais adaptável injetando diferentes provedores do mesmo tipo de serviço, conforme apropriado em diferentes circunstâncias. --angular.io

ng g service <name>
  • DRY
  • Manutenção
  • Facilidade para migrar para outras tecnologias

Os services geralmente são classes que reunem os métodos para serem utilizados pelos componentes.

Assim: -> Componente: interação usuário -> Serviço: cérebro, lógica do negócio, classes utilitárias.

Exemplo: TODO

Injeção de dependências

TODO


Rotas

TODO

app.component.html: add a tag router-outlet onde será renderizado o componente de rota

<router-outlet></router-outlet>

app-routing.module.ts:

import { NgModule } from "@angular/core";
// importar Routes e RoutersModule
import { Routes, RouterModule } from "@angular/router";
// importar ModuleWithProviders
import { ModuleWithProviders } from '@angular/core';

// importar componentes
import { LoginComponent } from "./login/login.component";
import { HomeComponent } from "./home/home.component";

// add path e o nome dos componentes
// que seriam o caminho/endereço e o componente que deverá ser renderizado para esse caminho
const routes: Routes = [
  // um caminho default
  { path: "", pathMatch: "full", redirectTo: "login" }
  // outras rotas
  { path: "login", component: LoginComponent },
  { path: "home", component: HomeComponent }
];

// para rotas principais
export const routing: ModuleWithProviders = RouterModule.forRoot(APP_ROUTES);

// rotas de funcionalidade usar forChild (ex: recipes-detail)
// export const routing: ModuleWithProviders = RouterModule.forChild(APP_ROUTES);

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}

no arquivo app.module.ts:

// importar o arquivo de rotas
import { routing } from './app.routing';

// ...
// add aos imports
@NgModule({
  // ...
  imports: [
    // ...
    routing
  ],
  providers: [],
  bootstrap: [AppComponent]
})

Adicionar link de rota (routerLink)

adicionar o routerLink com o caminho que foi criado no arquivo de rotas.

<a routerLink="/path">path</a>

<!-- exemplo -->
<a routerLink="/login">Login</a>

Rota com parâmetros

  • obter o parâmetro
  • subscribe
  • unsubscribe

o que vai mudar de um caso para o outro é o conteúdo de exemplo do arquivo welcome-page.component.ts.

app-routing.module.ts:

// ...
import { WelcomeComponent } from "./welcome/welcome.component";

// ...
  // rota com parâmetro rota/:parametro
  { path: "home/:welcome", component: WelcomeComponent }

// ...

no home.component.html:

<!-- exemplo -->
<input #name />

<a [routerLink]="['welcome', name.value]">Boas vindas</a>

Para exibir um valor na tela, supondo que temos um input onde o usuário digita seu nome na página home (home.component.html):

welcome-page.component.html:

<h2>Olá, {{ name }}, seja bem vinda (o)!</h2>

Obter parâmetro

welcome-page.component.ts:

import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";

@Component({
  selector: "app-welcome-page",
  templateUrl: "./welcome-page.component.html",
  styleUrls: ["./welcome-page.component.css"]
})
export class WelcomePage implements OnInit {
  name: string;

  constructor(private route: ActivatedRoute) {
    console.log(this.route.snapshot.params["name"]);
    this.id = this.route.snapshot.params["name"];
  }

  ngOnInit() {}
}

Subscribe e unsubscribe

welcome-page.component.ts:

import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
// add import do Subscription
import { Subscription } from "rxjs";

@Component({
  selector: "app-welcome-page",
  templateUrl: "./welcome-page.component.html",
  styleUrls: ["./welcome-page.component.css"]
})
export class WelcomePage implements OnInit {
  name: string;
  // add uma variável do tipo Subscription
  subscription: Subscription;

  constructor(private route: ActivatedRoute) {}

  // subscribe
  ngOnInit() {
    this.subscription = this.route.params.subscribe((params: any) => {
      this.name = params["name"];
    });
  }

  // unsubscribe
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

Rotas imperativas

TODO

import { ActivatedRoute, Router } from "@angular/router";

// exemplo, faz a validação do dado e redireciona para outra página
if (this.name == null) {
  this.router.navigate(["/error"]);
}

!não esquecer que todos os caminhos devem estar declarados no aquivo de rotas.

Definir parâmetro de url (query)

Exemplo:

<!-- !nota: 'notebook' está entre aspas simples, porque é uma string -->
<button routerLink="/produtos" [queryParams]="{prod:'notebook'}">
  Produtos
</button>

<button routerLink="/produtos" [queryParams]="{pagina:21}">Produtos</button>

Assim, quando clicado neste botão, irá acrescentar os parâmetros solicitados na url da página: http://localhost:4200/produtos?pagina=notebook

Extrair parâmetro da url

Exemplo:

Um botão que ao ser clicado muda para a próxima página.

<button (click)="nextPage()">Próxima página</button>

em produtos.components.ts:

// acrescentar os imports necessários
import { Subscription } from "rxjs";
import { ActivatedRoute, Router } from "@angular/router";

@Component({
  selector: "app-produtos",
  templateUrl: "./produtos.component.html",
  styleUrls: ["./produtos.component.css"]
})
export class produtosComponent implements OnInit {
  page: number;
  subscription: Subscription;

  constructor(private activatedRoute: ActivatedRoute, private router: Router) {}

  ngOnInit() {
    this.subscription = this.activatedRoute.queryParams.subscribe(
      (queryParams: any) => {
        this.page = queryParams["page"];
      }
    );
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }

  nextPg() {
    // a expressão abaixo muda a url para outro caminho utilizando a classe Router no @angular/router
    // e vai somando 1 à página a cada vez que o botão é clicado
    // http://localhost:4200/produtos?page=2
    this.router.navigate(["/produtos"], { queryParams: { page: ++this.page } });
  }
}

Forms: Template Driven e Data Driven

!não esquecer de importar o FormsModule no módulo:

import { FormsModule } from "@angular/forms";
  • Template Driven:
    • orientado a template
    • criação, configuração e validação no HTML (template)
    • FormGroup criado pelo Angular do HTML
    • form submetido através do ngSubmit
  • Data Driven (Reativos)
    • orientado a dados
    • você programa o formulário e sincroniza com o DOM. A manipulação é feita via código no component.
    • criação, configuração e validação no componente
    • FormGroup no componente
    • não é necessário ngSubmit

Template Driven

TODO ex msg de erro TODO serValue TODO patch value TODO HTTP Post

Exemplo: no arquivo template-drive.component.html

<!-- criar var (ex #myForm) para referenciar o formulário -->
<!-- ngForm: angular passa a ajudar a gerenciar -->
<form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)">
  <div class="form-group">
    <label for="nome">Nome</label>
    <!-- acrescentar ngModel e um name para associação -->
    <input
      type="text"
      class="form-control"
      id="nome"
      name="nome"
      placeholder="Nome"
      ngModel
    />
  </div>

  <div class="form-group">
    <label for="email">Email</label>
    <input
      type="email"
      class="form-control"
      id="email"
      name="email"
      placeholder="nome@email.com"
      ngModel
    />
  </div>

  <button type="submit" class="btn btn-primary">Submit</button>
</form>

no arquivo template-drive.component.ts

import { Component, OnInit } from "@angular/core";

@Component({
  selector: "app-template-form",
  templateUrl: "./template-form.component.html",
  styleUrls: ["./template-form.component.css"]
})
export class TemplateFormComponent implements OnInit {
  user: any = {
    name: "Lays",
    email: "lays@lays.com"
  };

  onSubmit(form) {
    // retorna os valores associados através da referência name e ngModel
    console.log(form.value);
    // ou
    console.log(this.user);
  }

  constructor() {}

  ngOnInit() {}
}

Data Driven: Formulários reativos

no module.ts:

  • importar o ReactiveFormsModule
  • adicionar aos imports

no component.ts, importar a classe:

  • Importar as classes FormGroup e FormControl (ou FormBuilder)
  • criar uma variável do tipo FormGroup

arquivo app.module.ts (ou no módulo que for utilizar):

import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
// importar ReactiveFormsModule
import { ReactiveFormsModule } from "@angular/forms";

// ...

@NgModule({
  // ...
  imports: [
    // ...
    // add nos imports
    ReactiveFormsModule
  ]
  //...
})
export class AppModule {}

reactive-form.component.ts:

import { Component, OnInit } from "@angular/core";
// Importar a classe FormGroup e FormControl/FormBuilder
import { FormGroup, FormControl } from "@angular/forms";

@Component({
  selector: "app-data-form",
  templateUrl: "./data-form.component.html",
  styleUrls: ["./data-form.component.css"]
})
export class DataFormComponent implements OnInit {
  // declarar uma variável do tipo FormGroup
  form: FormGroup;

  //  maneira com FormBuilder
  constructor(private formBuilder: FormBuilder) {}

  ngOnInit() {
    this.form = this.formBuilder.group({
      name: ["Valor inicial nome"],
      email: ["Valor inicial email"]
    });
  }

  // ou
  // com new FormControl
  // constructor() { }
  // ngOnInit() {
  //   this.form = new FormGroup({
  //     name: new FormControl('Valor Nome Inicial'),
  //     email: new FormControl('Valor Email Inicial'),
  //   });
  // }
}

no html:

<!-- add a diretiva [formGroup]="variable name" para linkar com a variável que está no componente -->
<!-- add (ngSubmit)="onSubmit() vinculado ao component.ts -->
<form [formGroup]="form" (ngSubmit)="onSubmit()">
  <!-- ... -->
  <!-- add formControlName para linkar e atualizar o valor das variáveis do formBuilder de acordo com o input -->
  <input formControlName="email" />
  <!-- ... -->
</form>

Forms validation

Validation Angular.io

Nessa abordagem de formulário reativo, controlamos os forms pelo componente e não pelo template (DOM), por isso ao invés de simplesmente colocar um atributo required na tag HTML, fazemos isto através de código no componente.

Exemplo utilizando o Validators do @angular/forms:

  • importar a classe Validators
  • fazer as validações através do FormControl
    • new FormControl(defaultValueOfTheInput, Validators), se for mais de um tipo de validação, utilizar array: new FormControl(null, [Validators.required, Validators.email])

app.component.ts:

// importar a classe Validators
import { FormGroup, FormControl, Validators } from '@angular/forms';

// ...

ngOnInit() {
  // add validações ao FormGroup
  this.signupForm = new FormGroup({
    'username': new FormControl(null, Validators.required),
    'email': new FormControl(null, [Validators.required, Validators.email]),
    'gender': new FormControl('female')
  });
}

Podemos exibir mensagens adicionais no HTML para informar o usuário. Exemplo, um input de email:

<div class="form-group">
  <label for="email">email</label>
  <input type="text" id="email" class="form-control" formControlName="email" />
  <!-- utilizando ngIf -->
  <span
    *ngIf="!signupForm.get('email').valid && signupForm.get('email').touched"
    class="help-block"
    >Dado Inválido</span
  >
</div>

Custom Validation

Suponha um input, onde o usuário digita o nome de um produto, e queremos validar, se este produto está na lista de produtos que acabaram.

import { Component, OnInit } from "@angular/core";
// Importar as classes
import {
  FormGroup,
  FormControl,
  Validators,
  FormArray,
  FormBuilder
} from "@angular/forms";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
  invalidProducts = ["notebook", "mouse"];

  constructor(private formbuilder: FormBuilder) {}

  ngOnInit() {
    this.signupForm = new FormGroup({
      product: new FormControl(null, [
        Validators.required,
        this.invalidProducts.bind(this)
      ])
    });
  }

  onSubmit() {}

  // custom validator
  invalidProduct(control: FormControl): { [s: string]: boolean } {
    if (this.invalidProducts.indexOf(control.value) != -1) {
      return { productIsInvalid: true };
    }
    return null;
  }
}

Status Change

!Nota: para saber mais sobre este e outros FormControls, acesse angular.io.

this.signupForm.statusChanges.subscribe(status => console.log(status));
  • Pending
  • Valid
  • Invalid

Value Change

this.signupForm.valueChanges.subscribe(value => console.log(value));

Reset Values

Podemos limpar todos os dados digitados no campo de input após clicar em submit, por exemplo:

onSubmit() {
  this.signupForm.reset();
}

Neste caso acima o método reset() irá apagar o valor de todos os campos. É configurar para apagar campos específicos,

Exemplo, voltar o botão para o valor inicial de Enviar e desabilitado:

control.reset({ value: "Enviar", disabled: true });

SetValue

É possível atribuir um valor padrão de preenchimento de um campo de input com setValue, por exemplo:

this.signupForm.setValue({
  email: "test@test.com"
});

Com isso, o campo de input já virá previamente preenchido com email test@test.com, ainda será possível editá-lo.

Modificar/corrigir valor de input com PatchValue

Vamos supor que você queira modificar o input de email:

this.signupForm.patchValue({
  email: "seu-email@test.com"
});

Pipes (filtros)

<!-- {{ ... | pipe }} -->
<tag> {{ data | pipe }} </tag>

<!-- {{ ... | pipe:adicionais }} -->
<tag>{{ data | pipe: }}</tag>

Os pipes são utilizados para transformar/filtrar valores no template.

  • pura: não observa modificações no objeto
  • impura: observa modificações no objeto

Suponha um objeto product. Exemplo:

<!-- exibe o nome do produto em Uppercase (capitalizado) -->
<h1>{{ product.name | uppercase }}</h1>
<ul>
  <!-- exibe a quantidade no formato 00.00  -->
  <!-- onde (numero de casas antes da vírgula.minimoCasas-MaxCasas depois da vírgula -->
  <li>Quantidade: {{ product.amount | number: '2.2-2'}}</li>
  <!-- exibe o preço do produto no formato R$ 00.00 -->
  <li>Preço: {{ product.price | currency: 'BRL':true }}</li>
  <!-- exibe a data no formato dia-mes-ano -->
  <li>Validade: {{ product.date | date:'dd-MMM-yyyy' }}</li>
  <!-- exibe a composição do produto em formato JSON -->
  <li>Composição: {{ product.comp | JSON }}</li>
</ul>

Criar pipe customizado

ng g pipe

# ou

ng g p

!não esquecer de importar no módulo a pipe criada e adicionar nas declarations.

!o padrão é pure, para modificar este comportamento, é necessário modificar no arquivo de pipe.ts:

// ...
@Pipe({
  // add este metadado pure: false
  pure: false
})

Formato Local

Para modificar as configurações do projeto quanto a exibição de alguns dados filtrados pelo pipe:

exemplo, para exibição no formato brasileiro (ao invés de 1.99 ser 1,99):

no module.ts:

providers: [
  {
    provide: LOCALE_ID,
    useValue: "pt-BR"
    // useClass: '',
    // useFactory: ''
  }
];

Observables

= Data source (user imput, http requests, etc)

Transferência de dados assíncrona.

Observer:

  • Handle Data
  • Handle Errors
  • Handle Competition

Style Guide

Imports

imports do angular

// pula uma linha
outros imports (componentes, etc)

Importar JSON

  1. Criar arquivo json-typings.d.ts na pasta app e adicionar o código:
    declare module "*.json" {
      const value: any;
      export default value;
    }
  2. No arquivo component.ts importar o JSON, exemplo:
    import * as data from './data.json';
    !Nota: data, irá importar o módulo todo. Para acessar os dados do json em si, será necessário acessar o default do módulo: data.default.
  3. Atribuir valor a uma variável para utilizar no código, exemplo:
    users = data.default;