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
Pre-requisitos:
- NodeJS instalado
sudo npm i -g @angular/cli
sudo npm i -g typescript
ng new app-name
Entre no diretório do projeto para começar a trabalhar nele:
cd app-name
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
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
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)
Projeção de conteúdo:
<app-example>
<ng-content></ng-content>
</app-example>
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.
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
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 {}
(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 {}
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.
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:
- 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"];
}
}
- 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() {}
}
- no arquivo numbers.component.html:
<ul>
<li *ngFor="let num of nums">{{ num }}</li>
</ul>
Por fim, O resultado será:
- 1
- 2
- 3
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
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á:
- 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á:
!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"
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>
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>
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>
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>
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)
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>
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
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;
}
// ...
}
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.
ng lint
ng test
ng e2e
!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 sassless
para lessstyl
para stylus
Angular 2 categoriza diretivas em 3 partes:
- Diretivas com modelos conhecidos como Componentes
- Diretivas que criam e destroem elementos DOM conhecidos como Diretivas Estruturais
- Diretivas que manipulam o DOM alterando o comportamento e a aparência conhecidas como Diretivas de Atributo --codementor.io
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="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="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).
TODO
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>
É 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>
[class.prop]="value"
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>
[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"
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>
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) {}
}
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
{}
}
Exemplo:
<!-- elvis -->
<h1>{{ person?.name }}</h1>
<!-- é o mesmo que -->
<h1>{{ person != null ? person.name : '' }}</h1>
TODO
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
TODO
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 o routerLink
com o caminho que foi criado no arquivo de rotas.
<a routerLink="/path">path</a>
<!-- exemplo -->
<a routerLink="/login">Login</a>
- 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>
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() {}
}
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();
}
}
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.
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
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 } });
}
}
!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
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() {}
}
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>
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>
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;
}
}
!Nota: para saber mais sobre este e outros FormControls, acesse angular.io.
this.signupForm.statusChanges.subscribe(status => console.log(status));
- Pending
- Valid
- Invalid
this.signupForm.valueChanges.subscribe(value => console.log(value));
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 });
É 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.
Vamos supor que você queira modificar o input de email:
this.signupForm.patchValue({
email: "seu-email@test.com"
});
<!-- {{ ... | 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>
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
})
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: ''
}
];
= Data source (user imput, http requests, etc)
Transferência de dados assíncrona.
Observer:
- Handle Data
- Handle Errors
- Handle Competition
imports do angular
// pula uma linha
outros imports (componentes, etc)
- Criar arquivo json-typings.d.ts na pasta app e adicionar o código:
declare module "*.json" { const value: any; export default value; }
- No arquivo component.ts importar o JSON, exemplo:
!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:
import * as data from './data.json';
data.default
. - Atribuir valor a uma variável para utilizar no código, exemplo:
users = data.default;