Gerando Aplicações Angular FullStack – Parte 2

Criando o CRUD

Fala galera! No tutorial anterior criamos uma aplicação usando Angular, Express e Mongoose. Nesse tutorial vamos fazer um CRUD simples de cadastro de artigos, fazendo as chamadas pela nossa API. Começaremos configurando nosso banco de dados, depois vamos criar um formulário de cadastro de artigos seguindo cada passo do CRUD descrito abaixo.

CRUD é um acrônimo:

  • Create – cria novos registro.
  • Read – lê registros.
  • Update – atualiza registros.
  • Delete – deleta registros.

Basicamente, nossa aplicação vai poder criar um novo cadastro, ler os cadastros já registrados, atualizar um cadastro já existente e deletar um cadastro. Todo o código exibido nesse tutorial vai estar disponível no repositório do Github (se for testar, lembre-se de adicionar o arquivo .env na raiz do projeto conforme especificado no repositório).

A partir daqui, vamos precisar de um bom editor de texto. Sugiro que instale o Sublime, Atom ou Visual Studio Code. No meu caso, irei usar o Atom.

Configurando o Banco de Dados

Para esse tutorial, é preciso ter instalado o banco de dados MongoDB. O guia de instalação pode ser encontrado no próprio site do MongoDB (clique aqui). Caso não queira instalar o MongoDB, você pode optar por usar um DaaS, no próximo tutorial (Parte 3) iremos aprender como usa-lo mas já pode ir avançando e em seguida continuaremos por aqui.

Por padrão, o nome do banco de dados já vem como o mesmo nome do projeto, no caso, “meuapp”. Caso prefira alterar, a mudança pode ser feita alterando a propriedade MONGO_DB_URI  no arquivo de configuração .env na raiz do projeto.

 

Vamos testar nossa conexão com o banco? Dentro do diretório do projeto, execute no terminal (ou prompt) o comando.

npm run start

Deve aparecer algo parecido com isso:

 

Caso tenha algum problema na conexão, deve aparecer a mensagem abaixo. Nesse caso, o serviço deve estar inativo.

 

Crud – Cadastrando Artigos

Vamos seguir a sequência do acrônimo. Começando pela letra C (Create) onde vamos criar novos registros no banco de dados.

OBS.: Durante o desenvolvimento, recomento deixar o comando “npm run dev” executando para que a cada alteração feita no código reinicie nosso browser. Isso economiza bastante tempo do que ficar rebuildando a aplicação.

No terminal, vamos executar o comando abaixo para criar o componente do formulário, dentro da pasta pages do cliente.

ng generate component pages/form

 

Antes de começar a editar o formulário, vamos criar uma rota para o nosso componente. No arquivo “client/app/app.module.ts”, adicione a rota na variável “appRoutes”. Deve ficar desse jeito:

const appRoutes: Routes = [
 {
  path: '',
  component: HomeComponent
 },
 {
  path: 'form',
  component: FormComponent</strong>
 },
 { path: '**', component: NotFoundComponent }
];

Acesse http://localhost:8080/form e você deve ver a seguinte mensagem: form works!

 

Agora vamos partir para a construção do formulário. Nesse caso iremos usar o Bulma para estilizar nosso formulário, criando um cadastro básico de artigos composto apenas pelo título e corpo do texto.

Ctrl+C do código abaixo e Ctrl+V no arquivo “client/app/pages/form/form.component.html”.

<div class="field">
 <label class="label">Título</label>
 <div class="control">
  <input class="input" type="text" placeholder="Entre com o Título">
 </div>
</div>
<div class="field">
 <label class="label">Texto</label>
 <div class="control">
  <textarea class="textarea" placeholder="Texto..."></textarea>
 </div>
</div>
<div class="field">
 <div class="control">
  <button class="button is-link">Enviar</button>
 </div>
</div>

 

Assim deverá ficar o formulário:

 

Em seguida, vamos criar o modelo do formulário e criar a ação de enviar uma requisição para a nossa API para salvar o artigo no banco de dados.

No arquivo “client/app/pages/form/form.component.ts”, começaremos importando o serviço, instanciando o mesmo dentro do construtor. Aproveitando, já deixamos criada a ação do botão de enviar que chama operação do serviço para salvar. Evidentemente, ainda não criamos a função de salvar no nosso serviço.

client/app/pages/form/form.component.ts

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

import { AppService } from '../../services/app.service';

@Component({
 selector: 'app-form',
 templateUrl: './form.component.html',
 styleUrls: ['./form.component.css']
 })
 export class FormComponent implements OnInit {

constructor(public appService: AppService) {
 }

ngOnInit() {
 }

enviar() {
  this.appService.salvar();
 }

}

No serviço “client/app/services/app.service.ts”, criamos o objeto “form” no serviço e instanciamos o valor do título e do texto e adicione a função “salvar” logo em baixo do construtor recebendo um objeto do formulário como parâmetro. .

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

@Injectable()
export class AppService {

form: any = {};

constructor(private http: HttpClient) {
  this.form.title = "";
  this.form.body = "";
 }

salvar() {
 this.http.post('http://localhost:8080/api/artigo', this.form)
 .subscribe(
 res => {
  alert('Artigo Salvo com Sucesso!');
 },
 err => {
  console.error(err);
 }
 );
 }

}

Essa função irá enviar um POST com as informações do formulário, se não ocorrer erro deverá enviar um alerta com a mensagem “Artigo Salvo com Sucesso!”. Ainda não podemos testar pois temos que fazer o data binding.

Voltando ao “client/app/pages/form/form.component.html”, faça as seguintes modificações:

<div class="field">
 <label class="label">Título</label>
 <div class="control">
 <input class="input" type="text" placeholder="Título" [(ngModel)]="appService.form.title">
 </div>
</div>
<div class="field">
 <label class="label">Texto</label>
 <div class="control">
 <textarea class="textarea" placeholder="Texto" [(ngModel)]="appService.form.body"></textarea>
 </div>
</div>
<div class="field">
 <div class="control">
 <button class="button is-link" <strong>(click)="enviar()"</strong>>Enviar</button>
 </div>
</div>

 

Testando Cadastro de Artigos

Agora sim, podemos fazer nosso primeiro teste. Preencha os campos e pressione “Enviar”. Já é esperado um erro.

 

Como podem ver o POST foi enviado, porém retornou o código 404, ou seja, o endereço “/api/artigo” não foi encontrado.

Isso ocorreu pois ainda não criamos o endpoint “artigo”. O endpoint é um canal de comunicação onde nesse caso está relacionado ao “artigo”. Nesse projeto, ao criar um endpoint, ele já vem com as operações básicas de CRUD. Por isso, só iremos utilizar esse recurso usando as chamadas pelo cliente.

Para criar o endpoint, execute o comando no terminal e insira o nome do endpoint como “artigo”.

yo angular-api:endpoint

 

Vamos testar enviar o artigo novamente. It work’s!

 

cRud – Listando Artigos

Chegamos na letra R (Read), onde vamos buscar os dados no banco de dados e exibi-los ao usuário.

Já temos nossos artigos cadastrados no banco de dados mas queremos lista-los na página principal. Vamos aproveitar o componente “home” e apagar tudo dentro do arquivo “client/app/pages/home/home.component.html”.

Ctrl+C do código abaixo e Ctrl+V no arquivo “client/app/pages/home/home.component.html”.

<h1 class="title" *ngFor="let artigo of appService.artigos">
 <a href="/artigo/{{artigo._id}}">
 {{ artigo.title }}
 </a>
</h1>

 

No momento, não irá aparecer nada. Ainda temos que buscar nossos dados no banco de dados.

Pra isso, vamos adicionar a seguinte função no nosso serviço:

getArtigos() {
 this.http.get('http://localhost:8080/api/artigo')
 .subscribe(
 res => {
  console.log(res);
  this.artigos = res;
 },
 err => {
  console.error(err);
 }
 );
 }

 

Em “client/app/pages/home/home.component.ts” importe o serviço e parametrize o serviço no construtor que nem foi realizado no formulário. Dentro do construtor faça a chamada da função anterior para buscar todos os artigos.

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

import { AppService } from '../../services/app.service';

@Component({
 selector: 'app-home',
 templateUrl: './home.component.html',
 styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {

constructor(public appService: AppService) { 
  this.appService.getArtigos();
 }

ngOnInit() {
 }

}

 

Atualize a página inicial e ela deve estar com todos os artigos listados.

Como podem ver, a parte mais difícil talvez seja escolher nomes para os artigos… ^^’

 

Selecionando um Artigo

Já temos nossos artigos listados na página principal e agora quando o usuário clicar em um artigos queremos que ele seja redirecionado para outra página para ler seu conteúdo. Essa ainda é uma continuação do R (Read), onde buscamos por um artigo específico.

Vamos criar um novo componente chamado de “article”, que nem foi feito para o formulário.

ng generate component pages/article

 

Em “client/app/app.module.ts” criamos a nova rota para o componente. No entanto, nessa rota adicionamos um identificador do artigo declarado de “id”.

const appRoutes: Routes = [
 {
  path: '',
  component: HomeComponent
 },
 {
  path: 'form',
  component: FormComponent
 },
 {
  path: 'artigo/:id',
  component: ArticleComponent
 },
 { path: '**', component: NotFoundComponent }
];

 

Ctrl+C do código abaixo e Ctrl+V no arquivo “client/app/pages/article/article.component.html” para definir a estrutura do artigo. Aproveitando, declare as variáveis “titulo” e “conteudo” no serviço.

<h1 class=”title”>
{{ appService.titulo }}
</h1>
<hr>
<p>
{{ appService.conteudo }}
</p>

Em “client/app/services/app.service.ts”:

...
export class AppService {

 artigos: any = [];
 titulo: string = "";
 conteudo: string = "";

constructor(private http: HttpClient) { }
...

 

Ao clicar em algum artigo listado, ele possui um identificador que é definido na rota. Esse identificador será resgatado e será usado como parâmetro para buscar o artigo no banco de dados.

No arquivo “client/app/pages/article/article.component.ts” foram usados os módulos ActivatedRoute para resgatar o identificador e a função “getArtigoById” do serviço para buscar o artigo identificado.

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

import { AppService } from '../../services/app.service';

@Component({
 selector: 'app-article',
 templateUrl: './article.component.html',
 styleUrls: ['./article.component.css']
})
export class ArticleComponent implements OnInit {

constructor(public appService: AppService, public route: ActivatedRoute) { }

ngOnInit() {
  this.route.params.subscribe(params => {
   this.appService.getArtigoById(params['id'])
  });
 }

}

 

Adicione a função abaixo no serviço “client/app/services/app.service.ts”. Notem que é idêntico a função que busca todos os artigos, porém recebe um id como parâmetro e no final do endereço é adicionado o “id”.

getArtigoById(id) {
 this.http.get('http://localhost:8080/api/artigo/' + id)
 .subscribe(
 res => {
  console.log(res);
  this.titulo = res['title'];
  this.conteudo = res['body'];
 },
 err => {
  console.error(err);
 }
 );
 }

 

Com isso, já podemos ler o conteúdo do nosso artigo.

 

crUd – Editando o Artigo

Essa parte é representada pela letra U (Update) do CRUD, onde vamos poder editar as informações de um registro existente. Vamos começar adicionando os botões para editar e excluir no componente do artigo.

Modificando o “client/app/pages/article/article.component.html”.

<div class="field is-grouped pull-right">
 <div class="control">
  <button class="button is-link" (click)="editar()">
   <span class="icon"><i class="fa fa-edit"></i></span>
   <span>Editar</span>
  </button>
 </div>
 <div class="control">
  <button class="button is-danger" (click)="excluir()">
   <span class="icon"><i class="fa fa-trash"></i></span>
   <span>Excluir</span>
 </button>
 </div>
</div>
<h1 class="title">
 {{ appService.titulo }}
</h1>
<hr>
<p>
 {{ appService.conteudo }}
</p>

Modificando o “client/app/pages/article/article.component.ts”. Adicionando o móduto Router e definindo as funções de editar e excluir.

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

import { AppService } from '../../services/app.service';

@Component({
 selector: 'app-article',
 templateUrl: './article.component.html',
 styleUrls: ['./article.component.css']
})
export class ArticleComponent implements OnInit {

id: any;

constructor(public appService: AppService, public route: ActivatedRoute, public router: Router) { }

ngOnInit() {
  this.route.params.subscribe(params => {
  this.id = params['id'];
  this.appService.getArtigoById(this.id)
 });
 }

editar() {
  this.router.navigate(['/form/'+this.id]);
 }

excluir() {
  this.appService.deleteArtigo(this.id);
 }

}

Adicione a função abaixo no serviço “client/app/services/app.service.ts”. Já estamos adiantando a última parte (spoiler).

deleteArtigo(id) {
 this.http.delete('http://localhost:8080/api/artigo/' + id)
 .subscribe(
 res => {
 console.log(res);
  alert('Artigo Removido com Sucesso!')
 },
 err => {
  console.error(err);
 }
 );
 }

Ao tentarmos editar algum artigo ocorrerá um erro (imagem abaixo). Isso ocorre devido que a URL “http://localhost:8080/form/5a931498a05b5d211ae96542” ainda não possui rota. Vamos consertar isso.

 

Vamos adicionar uma nova rota em “client/app/app.module.ts”.

const appRoutes: Routes = [
 {
  path: '',
  component: HomeComponent
 },
 {
  path: 'form',
  component: FormComponent
 },
 {
  path: 'form/:id',
  component: FormComponent
 },
 {
  path: 'artigo/:id',
  component: ArticleComponent
 },
 { path: '**', component: NotFoundComponent }
];

 

A rota criada é muito parecida com a rota “/form”, porém a rota “form/:id” especifica um id como parâmetro. Assim, o “id” pode ser resgatado pelo construtor do componente “form” e buscar os dados do artigo especificado. Vamos fazer algumas modificações no componente “form”.

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

import { AppService } from '../../services/app.service';

@Component({
 selector: 'app-form',
 templateUrl: './form.component.html',
 styleUrls: ['./form.component.css']
})
export class FormComponent implements OnInit {

id: any;

constructor(public appService: AppService, public route: ActivatedRoute) {
 this.route.params.subscribe(params => {
 this.id = params['id'];
  if (this.id) {
   console.log('Modo Edição')
   this.appService.getArtigoById(this.id);
  } else {
   console.log('Modo Criação')
  }
 });
 }

ngOnInit() {
 }

enviar() {
 if (this.id) {
  console.log('Modo Edição')
  this.appService.editar(this.id);
 } else {
  console.log('Modo Criação')
  this.appService.salvar();
 }
 }

}

Nós verificamos caso tenha algum id disponível, se existir, será considerado “modo de edição”, caso não tenha um id, será considerado “modo de criação”. A função de enviar também foi alterada para que se possa enviar requisições tanto para salvar um registro quanto para editar um registro existente.

A função “editar” do serviço recebe como parâmetros o id e o serviço envia uma requisição do tipo PUT para o back-end alterar o formulário usando a id selecionada. Segue abaixo a implementação da função editar do serviço.

“client/app/services/app.service.ts”

editar(id) {
 this.http.put('http://localhost:8080/api/artigo/' + id, this.form)
 .subscribe(
 res => {
  alert('Alterado com Sucesso!');
 },
 err => {
  console.error(err);
 }
 );
 }

 

Vamos testar?

 

Se voltarmos para a página inicial (http://localhost:8080/), veremos que o registro selecionado foi editado conforme foram preenchidos os campos.

 

cruD – Removendo um Artigo

Chegamos a última parte do CRUD, letra D (Delete). Para remover um artigo, é bem simples. A função até foi implementada em um passo anterior. Recapitulando…

Essa função delete, implementada no serviço (“client/app/services/app.service.ts”), envia uma requisição do tipo DELETE com o id do artigo, deletando o artigo, caso ele exista.

deleteArtigo(id) {
 this.http.delete('http://localhost:8080/api/artigo/' + id)
 .subscribe(
 res => {
 console.log(res);
  alert('Artigo Removido com Sucesso!')
 },
 err => {
  console.error(err);
 }
 );
 }

 

Conclusão

Uffa! Temos agora nossa aplicação realizando o CRUD com o banco de dados! O código fonte do projeto pode ser encontrado no repositório do Github.

Tais requisições de CRUD, são realizadas pelo serviço do Angular e enviadas para o servidor Express onde todas as rotas da API estão previamente configuradas. Todas as rotas utilizadas nesse tutorial podem ser encontradas no arquivo “server/model/artigo/router.js”, que foi criado ao gerarmos nosso endpoint. A princípio, não demos muita atenção para o back-end em si, porém é um outro universo de grande importância e com sua própria complexidade e que pode ser abordado em outro tutorial.

Esse tutorial foi bem extenso. Espero que tenham conseguido seguir os passos e extrair algo disso. Muitos passos foram resumidos até para não desfocar do objetivo. Qualquer dúvida, sugestões, críticas são bem-vindas.

No próximo tutorial, iremos conectar nossa aplicação com um banco de dados MongoDB remoto para que ele esteja disponível para o nosso aplicativo quando ele estiver no Heroku.

 

Links Rápidos:

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *