Uma introdução rápida ao Angular
Tags: angular, pt, portugues, javascript
Published: 2020-10-04 12:36:17

OBS: Este texto está desatualizado, a versão mais refinada está publicada em: https://wkrueger.gitbook.io/angular/

Angular é a última framework de frontend que aprendi. Antes de trabalhar com ela, eu tinha um pouco de receio de aprendê-la pois a documentação inicial parecia um tanto assustadora. Por outro lado, após começar a trabalhar com ela, vi que não é tão complicada assim, a documentação é que não ajuda...

Neste texto, tento trazer um apanhado BEM resumido de como fazer algumas tarefas comuns no Angular, adicionando links para as fatias relevantes da documentação oficial.

O texto presume conhecimento prévio de desenvolvimento web (HTML/CSS e JS). Também não explico aqui conceitos do Typescript, mas tendo conhecimento em JS dá pra entender o texto.

## Ferramentas Recomendadas

- Node.js
- VS Code
- Plugin "Angular Language Service" para o VS Code
- Plugin "angular inline 2"

## Comparação

**Onde o Angular pode parecer mais fácil, se comparado ao React**

> (alguns comentários daqui são voltados a quem já lidou com React)

- O uso de _templates_ HTML e folhas de estilo (ao invés de JSX e CSS-in-JS) é algo mais familiar a pessoas com experiência prévia de web;
- (afirmo aqui que) O uso do padrão de injeção de dependências é mais simples e eficiente no gerenciamento de estado e na escrita de mocks para testes se comparado a alternativas populares do React (Redux);
- Não é necessário se preocupar em fazer alterações imutáveis de estado (na maioria das situações); O re-render é mais "automágico";
- A framework abstrai para si a complexa configuração de build e "code splitting", você geralmente não toca em configuração de webpack;
- Módulos sugeridos de formulários e roteador podem não ter a melhor experiência, mas são estáveis;


# Iniciando

- Instale o Angular CLI `npm install -g @angular/cli`
- Crie um esqueleto de projeto `ng new <projeto>`
- **Inicie a aplicação com `npm start`**

## Estrutura

![Estrutura inicial](https://dev-to-uploads.s3.amazonaws.com/i/l5vkupd2evr42lmumb83.PNG)

## Terminologia

Sempre quando falo em _template Angular_, me refiro a quando se escreve sintaxe de layout (similar ao HTML).

Esta sintaxe é parecida mas não é exatamente HTML pois possui recursos adicionais. O navegador não reconheceria.

# Módulos

O ponto de entrada de uma aplicação Angular é o arquivo main.ts (gerado pelo CLI). Esta aponta para o módulo raiz (no caso abaixo, `AppModule`).

```ts
// main.ts
// ...
platformBrowserDynamic()
  .bootstrapModule(AppModule) // <-- AppModule é o módulo de entrada
  .catch((err) => console.error(err))
```

O módulo raiz aponta que o componente raiz será o `AppComponent`, via propriedade `bootstrap`:

```ts
// app/app.module.ts
// ...
import { AppComponent } from "./app.component"

@NgModule({
  declarations: [AppComponent], //Componentes e diretivas aqui
  imports: [BrowserModule, AppRoutingModule], // Outros módulos aqui
  providers: [], // Serviços aqui
  bootstrap: [AppComponent], // AppComponent é o módulo de entrada
})
export class AppModule {}
```

- Componentes do Angular devem ser registrados na propriedade `declarations` para estarem disponíveis em templates.
- A aplicação pode ser dividida em submódulos para podermos configurar o _code splitting_ (não é o intuito desse texto). O _code splitting_ é usado pra dividir sua aplicação em partes menores e evitar que o browser fique 1 minuto parado em uma tela branca pra carregar um JS de 15MB.

Docs: [introduction to modules](https://angular.io/guide/architecture-modules).

# Componentes

No Angular, um componente é um bloco que une:

- Um template "Angular HTML";
- Uma folha de estilos com escopo isolado (CSS ou SCSS);
- Um arquivo TS para os metadados, estado e a lógica do componente.

O template "Angular HTML" aceita sintaxes específicas do Angular, dentre elas:

- Outros **componentes** podem ser incluídos a partir de suas tags ex: `<meu-componente></meu-componente>`;
- **Diretivas** do Angular podem criar propriedades personalizadas de elementos. Ex: `<div tooltipText="Hello world"></div>`

Um componente é iniciado a partir de uma classe anotada com `@Component`. As propriedades **selector** e **template(Url)** são obrigatórias.

```ts
import { Component } from "@angular/core"

@Component({
  selector: "app-root",
  template /* ou templateUrl */: `<p>Olá mundo</p>`,
  styleUrls /* ou styles */: ["./app.component.scss"],
})
export class AppComponent {
  algumValor = "Olá mundo"
  umaLista = ["um", "dois", "três"]
}
```

- O atributo `selector` indica como este componente será chamado dentro do template.
- No arquivo de módulo, este componente deve ser registrado na chave `declarations`;
- O template e os estilos podem ser declarados ou "em linha" (`template`) ou em outro arquivo (`templateUrl`).

Docs: [introduction to components](https://angular.io/guide/architecture-components);

# Sintaxe do template Angular

- Alguns dos recursos suportados pelo template Angular:

**Interpolação de variáveis**

- Qualquer **propriedade da classe** de componente é disponível no template.
- Para interpolação usam-se chaves duplas.

```html
<p>Interpola: {{ algumValor }}</p>
```

**Passando atributos**

```html
<!-- 1 -->
<componente entrada="algumValor"></componente>
<!-- 2 -->
<componente [entrada]="umaLista"></componente>
<!-- 3 -->
<componente (saida)="algumaFuncao($event)"></componente>
<!-- 4 -->
<componente [(twoWay)]="variavel"></componente>
```

1. Passa a **string literal** `"algumValor"` para o parâmetro;
2. Passa a **propriedade** declarada na classe para o parâmetro (no caso, umaLista= ["um", "dois", "três"])
3. Quando componentes emitem eventos, usam-se parênteses. Ex: `(click)`, `(hover)`, `(submit)`;
4. A sintaxe `[(twoWay)]` é um atalho para `[twoWay]="variavel" (twoWayChange)="variavel = $event"`;

Ver [property binding](https://angular.io/guide/property-binding);

**Loops**

```html
<div *ngFor="let item of lista">{{ item }}</div>
```

**Condicionais**

```html
<div *ngIf="algumValor"></div>
```

Ver [structural directives](https://angular.io/guide/structural-directives);

**CSS condicional**

```html
<div [class.active]="isActive"></div>
```

Adiciona a classe `active` se a variável for verdadeira.

Mais informações em [attribute, class and style bindings](https://angular.io/guide/attribute-binding);

**Referências**

- Elementos em um template podem ser referenciados na respectiva classe com a anotação `@ViewChild()`;
- Anotações com `#` são usadas pra auxiliar referências.

```ts
// date-picker.component.ts
@Component({ selector: 'date-picker', ... })
export class DatePickerComponent {

  pickDate() {
    console.log('date picked')
  }

}
```

```ts
// app.component.ts
@Component({
  template: `
    <date-picker #datePicker></date-picker>
    <div #theDiv>Hello</div>
  `,
})
export class AppComponent {
  @ViewChild("datePicker") datePickerElement1!: DatePickerComponent
  // ou
  @ViewChild(DatePickerComponent) datePickerElement2!: DatePickerComponent

  @ViewChild("theDiv") divElement!: ElementRef

  ngAfterViewInit() {
    this.datePickerElement1.pickDate()
  }
}
```

Observe que, para a anotação de tipo (que é **opcional**) usa-se:

- A própria classe do componente (`DatePickerComponent`), quando o elemento referenciado é um componente Angular;
- `ElementRef` quando é um elemento HTML qualquer;
- `TemplateRef` quando é uma tag `<ng-template>`

**ng-container**

Se deseja aplicar *ngIf ou *ngFor sem criar uma div para isso, use ng-container.

```html
<ng-container *ngFor="let num of umaLista">{{ num }}</ng-container>
```

**ng-template**

Os elementos dentro de um `ng-template` não são diretamente renderizados. Ele é usado para passar um bloco de HTML como variável a um componente ou função (exemplo: um modal). Você verá ele sendo usado em bibliotecas de interface de usuário (ex: Material, Bootstrap, etc).

Exemplo (abaixo): Uma biblioteca especifica a diretiva `hoverPopup` que recebe como entrada uma seção de template. Ao passar o mouse por cima deste componente, um popup com este HTML é exibido.

```html
<ng-template #popup>
  <p>Bem vindo</p>
</ng-template>

<label [hoverPopup]="popup">Exibir</label>
```

Mais informações na [documentação do template Angular](https://angular.io/guide/template-syntax).

## Notas sobre estilos

- Estilos de componentes possuem escopo restrito e **isolado** a esses componentes. Isto significa que componentes filho, por padrão, não recebem o estilo do componente pai;
- Este comportamento pode ser burlado com o seletor `::ng-deep` \*;

> (\*) O seletor ng-deep é deprecado mas não tende a ser substituído até que um equivalente seja oficialmente publicado nas especificações do CSS.

```css
.componente-filho ::ng-deep svg {
  stroke: black;
}

:host {
  /* estilos *deste* componente */
  display: block;
}
```

- O seletor `:host` é usado pra aplicar estilos à raiz do componente;

- Além dos estilos isolados de componente, o projeto também conta com **estilos globais**, que são os presentes na raiz `src/styles.scss`.

# Fluxo de dados

Duas das principais formas de trasmitir dados por uma aplicação Angular são:

- Propriedades de entrada e saída de componentes;
- Injeção de dependência (serviços);

## Propriedades de entrada e saída

**Entrada**

Anote uma propriedade com `@Input()` para amarrá-la a uma entrada do componente.

```ts
@Component({
  selector: "app-some-component",
  template: `<button type="button">{{ texto }}</button>`,
})
export class SomeComponent implements OnChanges {
  @Input() texto = ""

  ngOnChanges(changes) {
    // fazer algo
  }
}
```

```ts
@Component({
  selector: "app-consumer",
  template: `<app-some-component texto="Clique aqui"></some-component>`,
})
export class ConsumerComponent {}
```

- No exemplo acima, um botão é desenhado com o conteúdo "Clique aqui".
- O método opcional `ngOnChanges` é chamado sempre que uma `@Input()` sofrer alteração.
- A interface (também opcional) `implements OnChanges` trás ajuda de tipos para o método `ngOnChanges`.

**Saída**

Um componente envia sinais de saída a partir de `EventEmitter`s anotados com `@Output()`;

- Ao escrever `EventEmitter`, o editor dará várias sugestões. Selecione a pertencente ao `@angular/core`.

Emissor:

```ts
@Component({
  selector: "app-output",
  template: `<button type="button" (click)="processarClique($event)">Click me</button>`,
})
class OutputComponent {
  @Output() fuiClicado = new EventEmitter<Date>()

  processarClique(ev) {
    this.fuiClicado.emit(new Date())
  }
}
```

Consumidor

```ts
@Component({
  selector: "app-consumer",
  template: `<app-output (fuiClicado)="tratar($event)"></app-output>`,
})
class ConsumerComponent {
  tratar(ev) {
    console.log(ev) // irá logar a Data atual
  }
}
```

Ver [inputs and outputs](https://angular.io/guide/inputs-outputs).

## Serviços e injeção de dependência

Uma classe anotada com `@Injectable()` pode ser **atrelada** a um módulo ou componente;

- Você define a quem o "injetável" está atrelado passando a classe ao atributo `providers` do componente ou módulo;
- Por exemplo, se você passar o serviço `MeuServico` ao `providers` do componente `MeuComponente`, uma instância desse serviço (`new MeuServico()`) será criada para cada `MeuComponente`. Quando `MeuComponente` for destruído, a instância do serviço também é destruída e é invocado o método `ngOnDestroy()`;
- Se você passar um serviço ao módulo raiz, este serviço efetivamente será um _Singleton_ (instância global).

```ts
@Injectable()
class MeuServico {
  dizerAlgo() {
    console.log('algo')
  }
}

@Module({ 
  ...,
  providers: [MeuServico]
})
class MeuModulo {}

@Component({ 
  ...,
  providers /* ou viewProviders */: [MeuServico]
})
class MeuComponente {}
```

**Consumindo o serviço**

Acesse o serviço o passando como parâmetro no contrutor da classe.

```ts
@Component(...)
class MeuComponente {

  constructor(private meuServico: MeuServico) {}

  aoClicar() {
    this.meuServico.dizerAlgo()
  }
}
```

- Diz-se aqui que uma instância de `MeuServico` foi **"injetada"** em `MeuComponente`;

- Caso o serviço não tenha sido especificado em nenhuma chave `providers`, o Angular vai reclamar;

- Caso o serviço tenha sido providenciado em vários lugares (no módulo e no componente), a instância mais local (a do componente) é fornecida;

**providers vs. viewProviders**

Serviços fornecidos pela chave `providers` de um módulo são acessíveis em todos os componentes deste módulo.

Por outro lado, quando um serviço é passado na chave `providers` de um componente, ele não é acessível para injeção nos componentes filho.

Quando o serviço é fornecido em um componente pela chave `viewProviders`, este é também acessível nos componentes filhos.

Serviços fornecidos a módulos e a _viewProviders_ trazem funcionalidade paralela ao que faz o _"context"_ no React.

**Qual é a utilidade desta complicação**

Além da funcionalidade de delimitação de contexto, a injeção de dependências é de grande utilidade em mocks para testes.

Quando uma classe especifica que quer consumir `ServicoA`, ela não necessariamente recebe a classe exata `ServicoA`. Você pode passar qualquer outra classe ao `providers` que atenda ao mesmo contrato. Ex: As ferramentas de teste permitem que se instanciem módulos injetando serviços "dublês".

Documentação: [introduction to services and dependency injection](https://angular.io/guide/architecture-services);

# Roteador

O Angular gera uma aplicação "single page", e o roteador é um componente importantíssimo neste contexto. O roteador permite com que a aplicação não seja totalmente recarregada quando se troca de página.

O que o roteador faz? Em resumo:

- Providencia um componente `<router-outlet>`;

Exemplo: No boilerplate padrão do Angular, o `<router-outlet>` é elemento único do componente raiz.

```ts
@Component({
  selector: "app-root",
  template: ` <router-outlet></router-outlet> `,
  styles: [],
})
export class AppComponent {}
```

- Solicita a configuração de um mapeamento entre URLs e:
  - Componentes ou
  - Módulos com subroteadores ou
  - Redirecionamentos

Exemplo

```ts
const routing = RouterModule.forRoot([
  { path: "", component: IntroComponent },
  { path: "gato/:id", component: GatoComponent },
  {
    path: "cachorro",
    loadChildren: () => import("./Cachorro/Cachorro.module").then((m) => m.CachorroModule), // usado para "code splitting"
  },
  { path: "capivara", children: [...] },
  { path: "**", redirectTo: '' }
])

@Module({
  ...
  imports: [routing, ...]
  ...
})
export class AppModule {}
```

- Sempre que um URL é alterado (ou no carregamento inicial de uma página), o componente correspondente é carregado no "outlet";
- Fornece os serviços a seguir que podem ser injetados:
  - `ActivatedRoute` pra determinarmos informações sobre o estado do roteador. Como: qual rota está ativada? Quais os parâmetros de URL?
  - `Router`, que é usado pra controlar o roteador (ir para...);

```ts
@Component({ ... })
class AlgumComponente {

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

  async ngOnInit() {
    const queryParams = await this.route.queryParams.pipe(take(1)).toPromise()
    console.log(queryParams)
  }

  goto() {
    this.router.navigate('/someroute', { queryParams: { hello: 'world' } })
  }

}
```

- O uso de links padrão do HTML (`<a href="/page"/>`) não é amigável pra SPA's pois fazem recarregar a página toda; Deve ser usada ao invés disso a diretiva `routerLink` fornecida pelo roteador.

```html
<a [routerLink]="['/hero', hero.id]">Goto hero</a>
```

- **Subroteadores e múltiplos outlets**: Conforme apontado anteriormente, é possível haver "roteadores filho". Neste caso haverá no HTML um `<router-outlet>` dentro de outro. Há também funcionalidade avançada onde um roteador pode controlar múltiplos _outlets_.

Mais informações no (extenso) [guia do roteador](https://angular.io/guide/router-tutorial-toh).

# O que mais falta

A idéia deste artigo é dar uma introdução rápida ao mais importante da framework, já que a documentação oficial pode ser um tanto intimidadora ao iniciante. Por este ser um breve resumo, muita coisa foi deixada de fora pra não tornar o artigo muito extenso. Alguns dos principais pontos adicionais para consultar futuramente (e seus respectivos links pra doc oficial):

- [Módulo de formulários](https://angular.io/guide/reactive-forms);
- [Publicando a aplicação](https://angular.io/guide/deployment);
- [Ciclo de vida do componente](https://angular.io/guide/lifecycle-hooks);
- Testes e2e e unitários
- Usando (ou fugindo do) RxJS;
- Usando (ou fugindo do) cliente HTTP do Angular;
- Dentre outros...

> (Doguinho aleatório da capa by Alexandru Rotariu from Pexels)

Muito obrigado!