Angular - alandrade21/docsCompartilhados GitHub Wiki
Nesta página:
O ecossistema para fazer o desenvolvimento de aplicações Angular envolve a instalação de 3 elementos distintos:
- Uma versão do Angular, e seu cliente de linha de comando.
- Uma versão do Typescript, e seu cliente de linha de comando.
- Uma versão do NodeJs.
Cada versão de Angular tem restrições bem específicas sobre as versões de Typescript e de NodeJs que são necessárias para seu funcionamento.
Verifique a tabela de versões compatíveis.
Registrar a versão compatível do node no arquivo packages.json
, através da entrada engines
, conforme mostrado abaixo:
{
...
"devDependencies": {
"@angular/cli": "~1.7.3",
...
"typescript": "~2.5.3"
...
},
"engines": {
"node": "~8.9.4"
},
...
}
Dessa forma, quem precisa evoluir ou manter várias aplicações Angular vai precisar, eventualmente, ter várias versões do Angular, do Typescript e do NodeJs em sua máquina, além de fazer o chaveamento entre essas versões.
- Instalar nodeJs
Veja página específica.
- Instala o angular cli localmente no projeto
npm i -D @angular/cli
- Se der loop infinito, instala o node-gyp e tenta de novo
npm i -D node-gyp
Essa instalação coloca o angular cli na pasta node_modules/.bin
do projeto. Para acessá-la sem ter que escrever todo o caminho, no arquivo package.json
do projeto crie um script como o mostrado abaixo:
{
...
"scripts": {
"ng": "ng",
"start": "ng serve",
...
}
}
Rode com npm run ng
.
- Instala o typescript
Veja página específica.
Digitar ng
no terminal retorna uma página de ajuda.
-
ng generate component <<nome-componente>>
: cria um novo componente no diretório atual ou caminho fornecido. -
ng g directive <<nome-diretiva>>
: cria uma nova diretiva no diretório atual ou caminho fornecido. -
ng g service <<nome-serviço>>
: cria novo serviço no diretório atual ou caminho fornecido. -
ng g module <<nome-modulo>>
: cria novo módulo no diretório atual ou caminho fornecido.
Imprime dado do model na view. {{nome-variavel}}
Dentro da interpolação pode estar escrito qualquer código java script que será interpretado e o resultado será impresso na tela.
Interpolação não deixa injetar HTML nem script. Trata tudo como texto plano.
Liga um campo da view com um dado da model. Two-way data binding.
<input [value]="data.title">
Esse exemplo liga o conteúdo da input ao campo title do objeto data da model. Qdo usa a sintaxe de colchetes no nome da propriedade, o conteúdo da propriedade é código js que será avaliado em tempo de execução.
Se quiser passar uma constante para a propriedade, faz assim: [value]="'texto plano'"
. Ou seja, coloca aspas simples dentro das aspas duplas. Isso é equivalente a não usar os colchetes.
Liga um evento do browser a um código da model.
<img (click)="onLogoClicked()" ...>
O código acima chama o método onLogoClicked()
da model no evento de click da imagem.
É possível dar nome a elementos do HTML.
<input #titleInput ...>
Agora esse input pode ser referenciado pelo nome titleInput
. Por exemplo, titleInput.value
retorna o conteúdo desse input. Template Reference aponta para o elemento do DOM que recebeu o nome.
Cria um novo com o comando CLI ng generate component <<nome-componente>>
. Cria no diretório atual.
Um componente é composto por um arquivo ts, um html e um css. No ts a classe é decorada com:
@Component({
selector: '<<nome do seletor html onde o componente será plotado>>',
templateURL: '<<caminho para o arquivo html com o html que será plotado no seletor>>',
styleUrls: ['<<caminho para o arquivo css com os estilos usados no template do componente>>']
})
Para passar dados para um componente, no ts do componente deve haver uma variável para receber esse input decorada com @Input
, por exemplo:
@Component({
selector: 'course-card',
templateURL: './course-card.component.html',
styleUrls: ['./course-card.component.css']
})
export class CourseCardComponent implements OnInit {
@Input()
title: string;
...
}
O dado a ser passado para o componente aparece no seletor desse componente. Por exemplo:
<course-card [title]="Core Deep Dive"></course-card>
Eventos de browser sobem pela árvore de componentes, mesmo tendo sido capturados em níveis mais baixos. Eventos customizados não.
Para disparar um evento customizado em um componente, primeiro é necessário definir o evento:
@Output()
courseSelected = new EventEmitter<Course>();
O evento deve ser decorado com @Output
para que ele possa sair do componente. O evento é um objeto da classe EventEmitter
do Angular, e o componente genérico é o elemento que será emitido no evento, no caso do exemplo, um objeto da classe Course
.
Em algum lugar no código do componente, esse evento precisa ser disparado. Por exemplo:
onCourseViewed() {
...
this.courseSelected.emit(this.course);
...
}
Esse tipo de evento é capturado no seletor do componente que o gera:
<course-card (courseSelected)="onCourseSelected($event)"></course-card>
No html, o elemento emitido é representado pelo $event
.
O nome do evento customizado é o mesmo da variável que define o emitter. Para mudar, pode ser fornecido um novo nome no decorator @Output
, assim: @Output('<<novo-nome>>')
.
<course-card *ngFor="let course of courses; index as i; first; last; even; odd"
[class.is-first]="first"
[curso]="course" [cardIndex]="i + 1">
</course-card>
Courses é uma variável presente no componente ts.
Para publicar o índice para ser usado no html, basta colocar a variável index
no ngFor. Ela pode ser renomeada com as alguma-coisa
.
first
é um booleano que informa se o elemento é o primeiro da interação. Usado para aplicar condicionalmente uma classe css. A sintaxe [class.is-first]=
espera receber um booleano. No caso de true
, aplica a classe ao elemento.
last
, de forma similar, identifica o último elemento.
even
e odd
identificam se a interação é par ou ímpar.
<img width="300" alt="Angular Logo" *ngIf="course.iconUrl"
[src]="course.iconUrl">
Se o elemento avaliado resultar em undefined
, a tag img não será montada no DOM da página resultante.
Casos de interpolation que usam atributos que podem ser nulos podem quebrar a carga de uma página. Um dos meios de evitar isso é utilizar o operador ?
, conforme mostrado abaixo:
<div class="course-title">
{{cardIndex + ' ' + course?.description}}
</div>
Outra forma de fazer a mesma coisa, impedindo que o operador ?
apareça em vários lugares do template, é utilizar uma div
externa que contenha toda a parte do template que dependa da propriedade que pode ser nula, e colocar um ngIf
nessa diretiva:
<div class="course-card" *ngIf="course">
<div class="course-title">
{{cardIndex + ' ' + course.description}}
</div>
...
</div>
É possível usar um else
no ngIf
:
<img width="300" alt="Angular Logo" *ngIf="course.iconUrl; else noImage"
[src]="course.iconUrl">
<ng-template #noImage>
<p>No image is available</p>
</ng-template>
Usa para aplicar uma classe css de forma condicional.
.course-card.beginner {
background: lightsalmon;
}
ngClass
pode receber uma string contendo nomes de classes css, um array contendo strings com nomes de classes ou um objeto de configuração no formato {nomeClasse: booleano}
. Faz o ngClass
chamar um método que retorne algum desses elementos.
<div class="course-card" *ngIf="course" [ngClass]="cardClasses()">
...
</div>
cardClasses() {
return {
'beginner': this.course.category == 'BEGINNER'
}
}
Outra maneira:
cardClasses() {
if (this.course.category == 'BEGINNER')
return 'beginner';
}
Usa para aplicar um estilo css de forma condicional.
Uma maneira de aplicar um estilo é:
<div class="course-title" [style.text-decoration]="'underline'">
{{cardIndex + ' ' + course.description}}
</div>
Essa forma escreve direto no DOM HTML, e não utiliza uma diretiva angular.
ngStyle
recebe um objeto de configuração:
<div class="course-title" [ngStyle]="{'text-decoration': 'underline'}">
{{cardIndex + ' ' + course.description}}
</div>
Para fazer condicional, chama uma função:
<div class="course-title" [ngStyle]="cardStyles()">
{{cardIndex + ' ' + course.description}}
</div>
Para os casos em que a decisão condicional tem mais que duas opções.
<div class="course-category" [ngSwitch]="course.category">
<div class="category" *ngSwitchCase="'BEGINNER'">Beginner</div>
<div class="category" *ngSwitchCase="'INTERMEDIATE'">Intermediate</div>
<div class="category" *ngSwitchCase="'ADVANCED'">Advanced</div>
<div class="category" *ngSwitchDefault>All Levels</div>
</div>
Quando não há uma tag que seja mãe de toda um estrutura que precisa ser acionada condicionalmente, é muito comum criar uma nova div
mãe apenas para aplicar as diretivas que devem incidir sobre todas as tags filhas.
Ao invés de criar uma div
, crie uma <ng-container></ng-container>
. Essa diretiva tem a vantagem de não gerar elementos DOM adicionais.
Funções de formatação de dados na tela.
<div>Start Date: {{startDate | date: 'dd/MM/yyyy'}}</div>
Se o pipe tiver vários argumentos, separa cada argumento com :
.
Pipes: uppercase, lowercase, titlecase, number (: '3.3-5' -> 3 dígitos inteiros e no mínimo 3 e no máximo 5 casas decimais), currency (: 'EUR' -> Qual moeda utilizar), percent, slice (:0:2 -> faz um slice numa coleção, começando no elemento 0 e terminando no elemento 2, não inclusivo), json (imprime uma coleção na forma de um objeto json. Útil para debug), keyvalue (aplicado a um objeto, cria um array com todas as propriedades do objeto e seus valores).
<div *ngFor="let pair of course | keyvalue">
{{pair.key + ': ' + pair.value}}
</div>
Um serviço é injetado no construtor de um componente:
constructor(private http: HttpClient){}
Serviços como HttpClient
, geralmente retornam um Observable
, que necessitam de um subscribe
para terem seus dados acessados. A maneira mais fácil de fazer esse subscribe é definida abaixo:
courses$: Observable<Course[]>;
...
this.courses$ = this.http.get<Course[]>(...);
Na variável courses$
, o $
indica que o conteúdo dessa variável é um observable. No template:
<course-card *ngFor="let course of (courses$ | async)">
</course-card>
O pipe async
faz o subscribe e fornece os valores ao template. Outra opção com *ngIf
:
<div *ngIf="(courses$ | async) as courses">
<course-card *ngFor="let course of courses">
</course-card>
</div>
A vantagem do uso de async pipe é que ele faz o unsubscribe do observable quando o componente é destruído, evitando memory leaks.
A classe de serviço deve ser decorada com @Injectable
.
@Injectable({
providedIn: 'root'
})
export class CourseService {
...
}
Isso significa que será criado um singleton dessa classe e esse singleton estará disponível na raiz do sistema de injeção de dependências. A partir desse momento, essa classe pode ser injetada nos construtores dos componentes.
Se o providedIn
não for informado, é necessário fornecer um provider
ao componente que necessita ter o serviço injetado. Isso é feito no decorator @Component
. Qdo um provider é fornecido a um componente, é criada uma instância da classe de serviço para cada instância do componente. Essa instância da classe de serviço está disponível para toda sub-árvore de componentes a partir do componente que recebeu o provider.
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls:['./app.component.css'],
providers: [
CourseService
]
})
Unidade organizacional que agrupa componentes, diretivas, pipes e serviços.
Um módulo pode ser pulicado independente de uma aplicação.
@NgModule({
declarations: [
AppComponent,
CourseCardComponent,
CourseImageComponent,
HighlightedDirective,
NgxUnlessDirective
],
imports: [
BrowserModule,
BrowserAnimationsModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
A seção declarations
contém tudo o que foi construído dentro do módulo.
imports
lista os módulos externos dos quais o código do módulo atual depende.
bootstrap
identifica o componente que contém o seletor raiz a ser colocado no html, a partir do qual toda a árvore de componentes será montada.
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
...
}
<body>
<app-root></app-root>
</body>
Se o index.html possuir mais de um seletor, então a seção bootstrap
terá mais de um elemento.
Ao criar um módulo, é necessário dizer quais as partes desse módulo são visíveis fora dele. Para isso, é preciso colocar a propriedade exports: []
na @NgModule
com os elementos da declarations: []
que são públicos.
No index.html
coloca a tag <case href="/">
. Essa tag aponta para o ponto de entrada da aplicação.
Vai no AppModule
(root module), e importa o router:
@NgModule({
declarations: [
App,
LessonsList,
CoursesList
],
imports: [
BrowserModule,
RouterModule.forRoot(routeConfig)
],
bootstrap: [App]
})
export class AppModule { }
Vai no html do componente raiz (App) e coloca <router-outlet></router-outlet>
na posição da página onde os outlets das rotas devem ser injetados. Outlets são as seções que serão injetadas na página quando a rota mudar.
No arquivo router-config.ts
é definido um array de rotas. Cada rota é um objeto contendo um path e um componente a ser inserido para aquela rota.
export const routeConfig = [
{
path: 'home',
component: Home
}
]