This project was generated with Angular CLI version 17.3.6..
https://github.com/leosasantos/porto-digital-agenda/archive/refs/heads/router.zip
- Criar projeto do github
- Configurar chaves ssh
- Criar repositório inicial
- Criar projeto com angular cli
ng new porto-digital-agenda --no-standalone --routing --ssr=false
- Realizando commit inicial
git add .
git commit -m "Initial commit"
git remote add origin <remote_repository_url>
git push -u origin master
- Instalar biblioteca
npm install bootstrap
- Configurar css no angular.json
- Procurar no angular.json a propriedade "projects > <name_projeto> > architect > build > options > styles"
- Adicionar "node_modules/bootstrap/dist/css/bootstrap.css"
- Criando card
ng g c components/card
- Copiar código do card do bootstrap (https://getbootstrap.com/docs/4.0/components/card/) para card.component.html
- Ajustar código para contatos
- Ajustar app.component.html
- Criar interface contato com nome, telefone, email e tipo
ng g i models/contato
- Adicionar os tipos na inteface
nome: string;
telefone: string;
email: string;
tipo: number; // nacional -> 1 e internacional -> 2
- Configurando o card para Input
- Adicionando
@Input() contato: Contato|null = null;
em card.component.ts - Usando o contato no template (card.component.html)
<h5 class="card-title">{{ contato?.nome }}</h5>
<p class="card-text">Telefone: {{ contato?.telefone }}</p>
<p class="card-text">Email: {{ contato?.email }}</p>
- Usando o novo card com Input
- Definindo um contato em app.component.ts
contato: Contato = {
nome: "Fulano de Tal",
telefone: "81123456789",
email: "[email protected]",
tipo: 1
}
getContato(): Contato {
return this.contato;
}
- Usando no contato no card em app.component.html
<app-card [contato]="contato"></app-card>
- Interpolação
- Observar que já foi usado em card.component.html para os dados
{{ contato?.nome }}
{{ contato?.telefone }}
{{ contato?.email }}
- Property Binding
- Criar a rotina em card.component.ts
isContatoInternaciona(){
if(this.contato?.tipo == 1){
return false;
} else {
return true;
}
}
- Criar a div de informação de contato internacional
<div>
<!-- https://icons.getbootstrap.com/icons/pin-map-fill/ -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pin-map-fill" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M3.1 11.2a.5.5 0 0 1 .4-.2H6a.5.5 0 0 1 0 1H3.75L1.5 15h13l-2.25-3H10a.5.5 0 0 1 0-1h2.5a.5.5 0 0 1 .4.2l3 4a.5.5 0 0 1-.4.8H.5a.5.5 0 0 1-.4-.8z"/>
<path fill-rule="evenodd" d="M4 4a4 4 0 1 1 4.5 3.969V13.5a.5.5 0 0 1-1 0V7.97A4 4 0 0 1 4 3.999z"/>
</svg>
Contato internacional
</div>
- Adicionar
[hidden]="isContatoInternaciona()"
na div para controlar sua exibição
- Event Binding
- Criar a rotina de onClick no card.component.ts
onClick() {
alert('botão clicado!');
}
- Criar botão para acionar o evento em card.component.html
<button class="btn btn-primary" (click)="onClick()">
<!-- https://icons.getbootstrap.com/icons/trash3-fill/ -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash3-fill" viewBox="0 0 16 16">
<path d="M11 1.5v1h3.5a.5.5 0 0 1 0 1h-.538l-.853 10.66A2 2 0 0 1 11.115 16h-6.23a2 2 0 0 1-1.994-1.84L2.038 3.5H1.5a.5.5 0 0 1 0-1H5v-1A1.5 1.5 0 0 1 6.5 0h3A1.5 1.5 0 0 1 11 1.5m-5 0v1h4v-1a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5M4.5 5.029l.5 8.5a.5.5 0 1 0 .998-.06l-.5-8.5a.5.5 0 1 0-.998.06m6.53-.528a.5.5 0 0 0-.528.47l-.5 8.5a.5.5 0 0 0 .998.058l.5-8.5a.5.5 0 0 0-.47-.528M8 4.5a.5.5 0 0 0-.5.5v8.5a.5.5 0 0 0 1 0V5a.5.5 0 0 0-.5-.5"/>
</svg>
Excluir
</button>
- Criando uma lista de contatos em app.component.ts
- criando um array de contatos
- Removendo o contato
- Ajustando o função para retornar uma lista
- Ajustando o template para um valor do array (
contatos[2]
)
contatos: Contato[] = [
{
nome: "Fulano de Tal",
telefone: "81123456789",
email: "[email protected]",
tipo: 2
},
{
nome: "Beltrano de Tal",
telefone: "81234567981",
email: "[email protected]",
tipo: 2
},
{
nome: "Ciclano de Tal",
telefone: "81345678912",
email: "[email protected]",
tipo: 1
}
];
getContatos(): Contato[] {
return this.contatos;
}
- Criando uma container para listar
- Adicionando uma div container no component.html
<div class="container" style="display: flex"></div>
- Exibindo mais de um card em app.component.html
<app-card [contato]="contatos[0]"></app-card>
<app-card [contato]="contatos[1]"></app-card>
<app-card [contato]="contatos[2]"></app-card>
- Ajustando style do card para listar em card.component.html
<div class="card" style="width: 20rem; height: 13rem; margin: 1rem">
- Listar componentes com @for
- Atualizar o app.component.ts com o @for
@for (contato of contatos; track $index) {
<app-card [contato]="contato"></app-card>
}
- Exibir informação de contato internacional com @if
- Atualizar o componente card (card.component.html) com @if
@if (!isContatoInternaciona()) {
<!-- https://icons.getbootstrap.com/icons/pin-map-fill/ -->
<div>
...
</div>
}
- Criar um serviço para recuperar contatos
ng g s services/contatos
- Transferindo o array de contatos de app.componnent.ts para o service e implementando o serviço de recuperar contatos
contato.service.ts
contatos: Contato[] = [
{
nome: "Fulano de Tal",
telefone: "81123456789",
email: "[email protected]",
tipo: 2
},
{
nome: "Beltrano de Tal",
telefone: "81234567981",
email: "[email protected]",
tipo: 2
},
{
nome: "Ciclano de Tal",
telefone: "81345678912",
email: "[email protected]",
tipo: 1
}
];
public recuperarContatos(): Contato[] {
return this.contatos;
}
- Removendo o array e adicionando o serviço no construtor e sua chamada em app.componnent.ts
constructor(
private contatosService: ContatosService
){ }
getContatos(): Contato[] {
return this.contatosService.recuperarContatos();
}
- Ajustando o app.component.html para chamar a nova rotina
@for (contato of getContatos(); track $index) {
- Criar o componente listar
- Criar o componente listar contatos
ng g c pages/listar
- Copiar o conteudo do listar de app.component.ts para .componennt.ts
export class ListarComponent {
constructor(
private contatosService: ContatosService
){ }
getContatos(): Contato[] {
return this.contatosService.recuperarContatos();
}
}
- Copiar o conteudo do container de app.component.html para listar.componennt.html
<div style="display: flex">
@for (contato of getContatos(); track $index) {
<app-card [contato]="contato"></app-card>
}
</div>
- Criar o componente incluir
ng g c pages/incluir
- Adicionando menu em app.component.ts
<header class="d-flex flex-wrap justify-content-center py-3 mb-4 border-bottom">
<a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto link-body-emphasis text-decoration-none">
<svg class="bi me-2" width="40" height="32"><use xlink:href="#bootstrap"></use></svg>
<span class="fs-4">Agenda Telefônica</span>
</a>
<ul class="nav nav-pills">
<li class="nav-item"><a href="#" class="nav-link">Listar</a></li>
<li class="nav-item"><a href="#" class="nav-link">Incluir</a></li>
</ul>
</header>
- Criando rotas
- Criar rotas em app-routing.module.ts
const routes: Routes = [
{ path: 'listar', component: ListarComponent },
{ path: 'incluir', component: IncluirComponent },
{ path: '', redirectTo: '/listar', pathMatch: 'full'}
];
- Adicionar renderização em app.component.html
<div class="container">
<router-outlet></router-outlet>
</div>
- Adicionar os links em app.component.html
<li class="nav-item"><a routerLink="/listar" class="nav-link">Listar</a></li>
<li class="nav-item"><a routerLink="/incluir" class="nav-link">Incluir</a></li>
- Criando Formulário Reativo (Reactive Form)
- Ir em app.module.ts e incluir ReactiveFormsModule nos imports.
import {ReactiveFormsModule} from '@angular/forms';
...
@NgModule({
...
imports: [
...
// outros imports ...
ReactiveFormsModule,
],
...
})
export class AppModule {}
- Ir em incluir.component.ts e incluir FormGroup e FormControl nos imports.
import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
@Component({
selector: 'app-incluir',
templateUrl: './incluir.component.html',
styleUrl: './incluir.component.css'
})
export class IncluirComponent {
form = new FormGroup({
nome: new FormControl(),
telefone: new FormControl(),
email: new FormControl(),
tipo: new FormControl(),
});
}
- Ir em incluir.component.html e incluir o texto abaixo, o nosso componente de inclusão ficará visível, apesar de pouco elegante.
<form [formGroup]="form">
<label>Nome:</label>
<input type="text" formControlName="nome">
<label>Telefone:</label>
<input type="number" formControlName="telefone">
<label>Email:</label>
<input type="text" formControlName="email">
<label>Tipo:</label>
<select formControlName="tipo">
<option value="2">Nacional</option>
<option value="1">Internacional</option>
</select>
<button type="submit">Incluir</button>
<button>Limpar</button>
</form>
- Ir em incluir.component.html e adicionar classes de estilo do Bootstrap, o nosso formulário ficará com um estilo bem melhor.
<div style="display: flex">
<form [formGroup]="form">
<div class="form-row">
<div class="col-12">
<label>Nome:</label>
<input type="text" class="form-control" formControlName="nome">
</div>
</div>
<div class="form-row">
<div class="col-12">
<label>Telefone:</label>
<input type="number" class="form-control" formControlName="telefone">
</div>
</div>
<div class="form-row">
<div class="col-12">
<label>Email:</label>
<input type="text" class="form-control" formControlName="email">
</div>
</div>
<div class="form-row">
<div class="col-12">
<label>Tipo:</label>
<select formControlName="tipo" class="form-control form-select">
<option value="2">Nacional</option>
<option value="1">Internacional</option>
</select>
</div>
</div>
<button type="submit" class="btn btn-primary">Incluir</button>
<button class="btn btn-secondary">Limpar</button>
</form>
</div>
- Ir em incluir.component.html e adicionar a submissão do formulário para o modelo.
<button type="submit" class="btn btn-primary"(click)="onSubmit()">Incluir</button>
- Ir em incluir.component.ts e observar apertando F12 no browser que ao submeter dados, os mesmos já estão disponíveis no modelo.
onSubmit(){
console.log(this.form.value)
}
- Ir em incluir.component.ts e evoluí-la, criando o objeto JSON que representa um contato e enviando ele para o serviço de inserção que será criado.
import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import { ContatosService } from '../../services/contatos.service';
@Component({
selector: 'app-incluir',
templateUrl: './incluir.component.html',
styleUrl: './incluir.component.css'
})
export class IncluirComponent {
constructor(
private contatosService: ContatosService
){ }
form = new FormGroup({
nome: new FormControl(),
telefone: new FormControl(),
email: new FormControl(),
tipo: new FormControl(),
});
onSubmit(){
let contato = {nome:this.form.value.nome,
telefone: this.form.value.telefone,
email: this.form.value.email,
tipo: +this.form.value.tipo,
}
this.contatosService.inserirContato(contato);
this.form.reset();
}
}
- Ir em contato.service.ts e evoluí-la, adicionando a funcionalidade de inserção. Basta voltar na listagem para observar que o contato foi incluído.
public inserirContato(contado: Contato){
this.contatos.push(contado);
}
- Criando seu pipe personalizado
- Ir no terminal e digitar o trecho abaixo para criar um pipe personalizado para colocar as letras maiúsculas.
$ ng g p capitalize
- Observe que foi criado o arquivo capitalize.pipe.ts, nele coloque o seguinte conteudo
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'capitalize' })
export class CapitalizePipe implements PipeTransform {
transform(value: any, args?: any): any {
let result = '';
for (const v of value.split(' ')) {
result += this.capitalize(v) + ' ';
}
return result;
}
private capitalize(value: string) {
return value.substring(0, 1).toUpperCase() +
value.substring(1).toLowerCase();
}
}
- Por fim ir em card.component.html e acionar o pipe criado
<h5 class="card-title">{{ contato?.nome | capitalize}}</h5>
Run ng serve
for a dev server. Navigate to http://localhost:4200/
. The application will automatically reload if you change any of the source files.
Run ng generate component component-name
to generate a new component. You can also use ng generate directive|pipe|service|class|guard|interface|enum|module
.
Run ng build
to build the project. The build artifacts will be stored in the dist/
directory.
Run ng test
to execute the unit tests via Karma.
Run ng e2e
to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
To get more help on the Angular CLI use ng help
or go check out the Angular CLI Overview and Command Reference page.