How to CRUD in Angular + Firebase Firestore

CRUD – Create, Read, Update, Delete – os quatro Santo Graal das ações do banco de dados. Elas são as únicas coisas que você precisará fazer qualquer coisa significativa com o seu banco de dados. Claro, você pode aumentar a complexidade das consultas, mas no final do dia, tudo se resume a essas quatro ações.

Firebase é um sistema baseado em nuvem de propriedade do Google que vem completo com ganchos de API, espaço de armazenamento de arquivos, sistema auth e capacidades de hospedagem. É um sistema muito subestimado que deve ser utilizado mais para prototipagem e desenvolvimento rápido de aplicações.

Se você quer inicializar uma aplicação web progressiva mas não tem a experiência de backend de configuração de servidores, criação de APIs e lidar com bancos de dados, então o Firebase faz uma opção fantástica para desenvolvedores front-end que podem se sentir isolados e atolados pela enorme colina de informações que eles têm que processar para até mesmo colocar sua aplicação em funcionamento.

Or se você tiver pouco tempo, o Firebase pode cortar suas horas de desenvolvimento quase pela metade para que você possa se concentrar na experiência do usuário e implementar esses fluxos de IU. Também é flexível o suficiente para migrar seu aplicativo front end e usar um banco de dados diferente se necessário.

Aqui está um guia rápido sobre como implementar ações CRUD com Angular e Firebase.

>

É um aplicativo de pedido de café com osso nu onde você pode adicionar pedidos (criar), listar pedidos do banco de dados (ler), marcar o pedido como concluído (atualizar) e remover um pedido (excluir).

O objetivo deste tutorial é ajudá-lo a começar com o Firebase Firestore e ver como é fácil se conectar e começar a usar o serviço de propriedade do Google. Este não é um anúncio para o Google (eu não recebo nenhum retorno deles por isso), mas apenas uma ilustração de como a Angular joga com o banco de dados.

O aplicativo que vamos fazer não é perfeito. Faltam coisas como validação de dados e um milhão de funcionalidades possíveis que eu também posso adicionar. Mas não é essa a questão. A questão é configurar em Angular o mais rápido possível e pô-lo a funcionar com uma base de dados em tempo real.

Então, já chega da introdução – aqui está o código passear.

A configuração inicial

Configure a sua aplicação Angular através da CLI Angular. Se ainda não tem a Angular CLI, pode saber mais sobre ela aqui.

Em suma, basta executar estes comandos no seu terminal no directório onde quer que a sua aplicação Angular se situe. Aqui estão os comandos e o que eles fazem.

npm install -g @angular/cli

Instala a CLI Angular no seu terminal se você ainda não a tem.

ng new name-of-app-here

Esta irá criar um novo projeto Angular usando a última versão do Angular disponível.

cd name-of-app-here

Quando você criar um novo projeto via CLI Angular, ele irá criar uma nova pasta de projeto para você. cd Levará você para essa pasta via terminal.

ng serve

Isto será iniciado e rodará seu projeto Angular.

Configurando Angular

Vamos criar 3 novas partes no total para esta aplicação Angular – pedidos, lista de pedidos e serviços compartilhados/ordenados. Os dois primeiros são componentes que irão conter a interface para o aplicativo e o serviço de compartilhamento/ordens que irá manter todas as chamadas da API do Firebase juntas.

Para criar os arquivos necessários, execute os seguintes comandos:

ng g c orders

g significa gerar e c significa componente. A última parte do comando é o nome do seu arquivo, que para o caso acima é chamado de ordens. Você também pode usar ng generate component orders para obter o mesmo efeito.

Criar outro componente para lista de ordens usando o seguinte comando.

ng g c order-list>

E finalmente, para o serviço, use s em vez de c como abaixo:

ng g s shared/orders

Isto irá criar um arquivo orders.service.ts em uma pasta chamada shared. Certifique-se de adicionar orders.service.ts em app.module.ts porque isso não é feito para você automaticamente como os componentes. Você pode fazer isso via import e adicioná-lo à lista providers assim:

import { OrdersService } from "./shared/orders.service";
...
providers:
...

O CSS

Para este tutorial, nós estaremos usando o Materialize CSS para fazer nossa aplicação final parecer melhor do que o css padrão. Não é necessário e você pode usar qualquer framework CSS ou o seu próprio CSS personalizado, se quiser. Você pode conferir os detalhes do Materialize CSS aqui.

Também vamos usar os ícones de material do Googles como botões para marcar o pedido de café como concluído ou deletar o pedido.

Uma forma de implementar isto é ter o código abaixo mesmo acima da etiqueta </head> no seu ficheiro index.html localizado na pasta src.

<!-- Compiled and minified Materialize CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<!-- Compiled and minified Materialize JavaScript -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script><!-- Google Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />

O código inicial da vista angular

Em app.component.html, apague todo o código inicial. Vamos usar o nosso próprio conteúdo para isto.

Vamos ligá-los ao componente que acabamos de criar e exibi-los na tela usando os selectores app-orders e app-order-list. Para fazer isso, escrevemos o código abaixo:

<div class="container">
<div class="row">
<app-orders class="col s6"></app-orders>
<app-order-list class="col s6"></app-order-list>
</div>
</div>

The container, row, col e s6 classes são parte do sistema de grid Materialize CSS. Todas as classes que você verá no resto deste tutorial são de Materialize CSS, a menos que mencionado em contrário.

Configurando o formulário

Para configurar os formulários, importe ReactiveFormsModule em app.module.ts e lembre-se de importá-lo no array imports.

import { ReactiveFormsModule } from "@angular/forms";

Inside orders.service.ts importe FormControl e FormGroup de @angular/forms e crie um novo formulário fora do constructor onde você pode configurar as propriedades do formulário como abaixo:

import { FormControl, FormGroup } from "@angular/forms";
...
...export class OrdersService {
constructor() {}
form = new FormGroup({
customerName: new FormControl(''),
orderNumber: new FormControl(''),
coffeeOrder: new FormControl(''),
completed: new FormControl(false)
})
}

Usaremos estes valores para armazenar no Firebase um pouco mais tarde neste tutorial.

Usando o formulário dentro de um componente

import a classe OrdersService no seu componente para que possa usar o objecto dentro do seu componente e depois criar um objecto da classe dentro do constructor.

import { OrdersService } from "../shared/orders.service";
...
constructor(private ordersService:OrdersService){}

Dentro do seu orders.component.html use a directiva formGroup para referenciar o objecto do formulário criado em OrdersService. Cada formControlName referencia os nomes que usamos no formGroup criado dentro da classe OrdersService. Isto permitirá que o controlador associado utilize as variáveis digitadas no formulário. Veja o código abaixo:

<form ="this.ordersService.form">
<input placeholder="Order Number"
formControlName="orderNumber"
type="text"
class="input-field col s12">
<input placeholder="Customer Name"
formControlName="customerName"
type="text"
class="input-field col s12">
</form>

No orders.component.ts, vamos configurar um array off coffee para looping through no nosso orders.component.html. Em teoria, você também poderia configurar isso dentro do seu banco de dados Firestore, fazer uma chamada para a coleção e depois usá-la. Mas para fins de duração, vamos configurá-lo como um array local. Dentro da sua classe OrdersComponent, configure o seguinte array:

coffees = ;

No seu orders.component.html e dentro das suas tags <form>, faça um loop através dele usando *ngFor com um manipulador de ações (click) para adicionar novos cafés ao seu pedido. Vamos exibir a lista de pedidos logo abaixo com a capacidade de remover cafés individuais como a seguir:

<button class="waves-effect waves-light btn col s4" 
*ngFor="let coffee of coffees"
(click)="addCoffee(coffee)">
{{coffee}}
</button><ul class="collection">
<li *ngFor="let coffee of coffeeOrder">
<span class="col s11"> {{ coffee }} </span>
<a class="col s1" (click)="removeCoffee(coffee)">x</a>
</li>
</ul>

Dentro de orders.component você cria um array vazio para abrigar o pedido de café, use a função addCoffee() para adicionar novos cafés, e removeCoffee() para remover uma bebida da sua lista de pedidos.

>

coffeeOrder = ;addCoffee = coffee => this.coffeeOrder.push(coffee);removeCoffee = coffee => {
let index = this.coffeeOrder.indexOf(coffee);
if (index > -1) this.coffeeOrder.splice(index, 1);
};

Missão do formulário de pedidos

Adicionar uma entrada de Submeter Pedido dentro e na parte inferior das etiquetas <form> e adicionar o onSubmit() a um manipulador de cliques como abaixo:

<form ="this.ordersService.form" (ngSubmit)="onSubmit()"> ...
<button
class="waves-effect waves-light btn col s12"
(click)="onSubmit()">
Submit
</button>
</form>

Criar um vazio onSubmit função no seu orders.component para o interino. Em breve adicionaremos o tratamento de eventos a ele. Neste ponto, o seu formulário deverá estar pronto para ser ligado à sua base de dados Firebase.

Configurar o componente de listagem

Agora só precisamos de um espaço para mostrar os nossos pedidos de café a partir da base de dados. Por enquanto, vamos apenas configurar o andaime html.

Navigate para o seu order-list.component.html e criar uma tabela com 3 cabeçalhos e 5 células de dados. As 3 primeiras células de dados irão conter os valores retirados do banco de dados e as 2 finais irão conter funcionalidades extras que lhe permitirão marcar o pedido como completo ou apagar o pedido.

>

Configurando o Firebase

>

Vá ao seu console Firebase e adicione um novo projeto.

Clique em ‘Add Project’ para criar um novo projeto.
>

Adicionar um nome de projeto, aceite os termos e condições e clique em create project.

Dê um nome ao seu projeto e aceite os termos controlador-controlador para criar um projeto.

Quando seu projeto Firebase tiver sido configurado, você verá algo como isto.

Criar uma base de dados selecionando Database no painel lateral (localizado em Develop) e depois clique em Create Database sob o banner Cloud Firestore.

Clique em ‘Create database’

Select start in test mode for security rules. Você pode modificá-lo mais tarde.

Recorde para manter suas credenciais na sua máquina local apenas para fins de segurança.
>

Você terá uma base de dados Firestore vazia como esta.

Conectando o Firebase Firestore ao Angular

Para conectar sua aplicação Angular ao Firebase, você precisará instalar os pacotes firebase e @angular/fire. Isto dar-lhe-á acesso a AngularFireModule e AngularFirestoreModule. Use o seguinte comando no seu terminal para instalá-los.

npm i --save firebase @angular/fire

Implementar os conectores

>

Volte ao seu console web Firebase e pegue os detalhes de configuração para usar na sua aplicação Angular. Isto está localizado em sua página de Visão Geral do Projeto. Parece algo como isto:

Terá de usar as suas próprias credenciais. A configuração acima não vai funcionar para você.

Copie a seção destacada do código, navegue até seu arquivo environment.ts (localizado dentro da pasta environments) e cole os detalhes da configuração dentro do objeto environment como firebaseConfig. Veja abaixo por exemplo:

export const environment = {
production: false,
firebaseConfig: {
apiKey: "xxx",
authDomain: "xxxx.firebaseapp.com",
databaseURL: "https://xxxx.firebaseio.com",
projectId: "xxxx",
storageBucket: "xxxx.appspot.com",
messagingSenderId: "xxxxx"
}
};

Importe os pacotes que você instalou anteriormente e o arquivo environment.ts para o seu app.module.ts.Você precisará inicializar o AngularFireModule com o firebaseConfig para o qual você acabou de fazer a configuração. Veja o exemplo abaixo:

import { environment } from "src/environments/environment";
import { AngularFireModule } from "@angular/fire";
import { AngularFirestoreModule } from "@angular/fire/firestore";
...
@NgModule({
...
imports:
...
})

Configurando o Firestore num serviço

Importe AngularFirestore para o seu ficheiro orders.service.ts e declare-o no construtor para que o seu serviço o conheça.

...
import { AngularFirestore } from '@angular/fire/firestore';
...
export class OrdersService {
constructor( private firestore: AngularFirestore ) {}
...
}

Agora está tudo preparado e pronto para começar na parte CRUD deste tutorial Angular + Firebase Firestore.

C é para Create

Para criar um novo registo na sua nova base de dados de Firestore, vai precisar de ligar para .add() . Nós vamos fazer isso dentro do arquivo orders.service.ts.

Para fazer isso, você vai precisar especificar o nome collection e quais dados você quer empurrar para a base de dados. No nosso caso, é coffeeOrders .

O código de exemplo abaixo usa uma promessa de retornar a chamada da Firebase e então permite que você decida o que quer fazer depois que tudo estiver feito.

...
createCoffeeOrder(data) {
return new Promise<any>((resolve, reject) =>{
this.firestore
.collection("coffeeOrders")
.add(data)
.then(res => {}, err => reject(err));
});
}
...

Para chamar esta função no seu componente, navegue até orders.component.ts e dentro do onSubmit() função e manipulador de ação, faça uma chamada a createCoffeeOrder() até ordersService.

O exemplo abaixo faz algum processamento dos dados mapeando a matriz coffeeOrder para o valor do formulário coffeeOrder. Eu também criei uma variável data para fins de desestruturação (juntamente com não ter que passar em um nome muito longo para createCoffeeOrder()).

...
onSubmit() {
this.ordersService.form.value.coffeeOrder = this.coffeeOrder;
let data = this.ordersService.form.value;
this.ordersService.createCoffeeOrder(data)
.then(res => {
/*do something here....
maybe clear the form or give a success message*/
});
}
...

E viola! Criou agora um registo da sua aplicação localhost Angular para a sua base de dados Firestore.

R é para ler

Para mostrar os dados das suas encomendas de café, vai precisar de uma função de leitura no seu orders.service.ts. O exemplo de código a seguir lhe dará todos os valores armazenados no seu coffeeOrders coleção.

...
getCoffeeOrders() {
return
this.firestore.collection("coffeeOrders").snapshotChanges();
}
...

Para usar esta função, você precisará chamá-la a partir do seu orders-list.component.ts . Você pode fazer isso importando o OrdersService para o arquivo e inicializá-lo no arquivo constructor.

...
import { OrdersService } from "../shared/orders.service";
...export class OrderListComponent implements OnInit {
constructor(private ordersService:OrdersService){}
...
}

Criar uma função para conter sua chamada e inicializá-la no arquivo ngOnInit() para chamá-la quando a vista for carregada pela primeira vez. Crie uma variável coffeeOrders para mapear os resultados retornados da sua base de dados via subscribe(). Usaremos isto para iterar e exibir em order-list.component.html

...
ngOnInit() {this.getCoffeeOrders();}
... coffeeOrders; getCoffeeOrders = () =>
this.ordersService
.getCoffeeOrders()
.subscribe(res =>(this.coffeeOrders = res));...

Para usar coffeeOrders no seu order-list.component.html , use *ngFor para laçar através do array retornado. Você também precisa fazer um pouco de inicio e fazer outro loop para a parte coffeeOrder para obter sua lista de cafés para cada cliente. Existem maneiras mais eficientes de fazer isso, mas para este tutorial, veja o código de exemplo abaixo:

...
<tbody>
<tr *ngFor="let order of coffeeOrders">
<td>{{ order.payload.doc.data().orderNumber }}</td>
<td>{{ order.payload.doc.data().customerName }}</td>
<td><span
*ngFor="let coffee of order.payload.doc.data().coffeeOrder">
{{ coffee }}
</span>
</td>
</tr>
</tbody>
...

E aí você o tem. Agora, você já conectou as capacidades de leitura à sua aplicação angular.

U é para Update

Vamos dizer que queremos ser capazes de marcar o pedido de café como completo no banco de dados e fazer algo ao item com base na mudança. Porque snapshot() mantém o controle de quaisquer alterações que aconteçam, você não precisa fazer nenhum controle ou sondagem no banco de dados.

Vamos criar outra célula de dados em nossa tabela com um ícone de ‘verificação’ do Google Materialize Icons e ligá-lo a um evento (click) que chamará a função markCompleted() no seu order-list.component.ts. Também vamos passar na ordem particular para este loop.

Vamos definir um atributo com o valor completed para a célula de dados para que ela possa determinar dinamicamente se queremos ter o ícone ‘check’ em exibição. Originalmente definimos este valor como false quando criamos o formulário pela primeira vez em orders.service.ts .

...
<td ="order.payload.doc.data().completed"
(click)="markCompleted(order)">
<i class="material-icons">check</i>
</td>
...

Inside order-list.component.ts , crie a função markCompleted() que usa o injetado data para passar para uma função chamada updateCoffeeOrder() em orders.service.ts.

...
markCompleted = data =>
this.ordersService.updateCoffeeOrder(data);
...

Inside orders.service.ts crie a função de manipulação. Esta função conectará e chamará sua base de dados Firestore com base na coleção selecionada e no id do documento. Já sabemos que nossa coleção é chamada de coffeeOrders e podemos encontrar o id do documento com base nos parâmetros passados na função componente call.

.set() irá definir o registro específico com quaisquer dados que você tenha passado. .set() aceita dois parâmetros – os seus dados e um objeto de configuração. Se você usar merge: true, então significa que você só atualiza o par de chaves de valor passado em vez de substituir o documento inteiro pelo que você passou em.

...
updateCoffeeOrder(data) {
return
this.firestore
.collection("coffeeOrders")
.doc(data.payload.doc.id)
.set({ completed: true }, { merge: true });
}
...

Agora quando você clicar no ícone ‘check’ na sua visualização, ele atualizará sua base de dados e desaparecerá porque seu atributo está agora definido para true.

D é para Delete

Para a acção final, vamos configurar tudo de forma semelhante ao processo de actualização – mas em vez de actualizar o registo, vamos apagá-lo.

Configure outra célula de dados com um manipulador de eventos de clique que chama uma função deleteOrder(). Vamos passar na instância do order dados no loop quando o ícone ‘delete_forever’ for clicado. Você vai precisar disto para o id do documento. Veja o código abaixo por exemplo:

...
<td ="order.payload.doc.data().completed"
(click)="deleteOrder(order)">
<i class="material-icons">delete_forever</i>
</td>
...

Inside your order-list.component.ts create the associated function that calls a deleteCoffeeOrder() function inside your orders.service.ts .

...
deleteOrder = data => this.ordersService.deleteCoffeeOrder(data);
...

Inside your orders.service.ts file, create the deleteCoffeeOrder() function and use the data injected to figure out what the document id is. Como atualizar, você precisa saber tanto o nome da coleção quanto o id do documento para identificar corretamente qual registro você deseja excluir. Use .delete() para dizer ao Firestore que você quer apagar o registro.

...
deleteCoffeeOrder(data) {
return
this.firestore
.collection("coffeeOrders")
.doc(data.payload.doc.id)
.delete();
}
...

Agora quando você clicar no ícone ‘delete_forever’, sua aplicação Angular irá disparar e dizer ao Firestore para apagar o registro específico. Seu registro desaparecerá da visualização quando o documento especificado for excluído.

Aplicação Final

Você pode encontrar o repositório Github para todo o projeto em funcionamento aqui. Você precisará criar seu próprio banco de dados Firebase e conectá-lo você mesmo, atualizando os arquivos de configuração. O código em si não é perfeito, mas eu o mantive no mínimo para que você possa ver como o Firestore funciona em uma aplicação Angular sem ter que passar por uma selva de código.

Tentei condensar o tutorial o máximo possível sem perder nenhum detalhe. Foi uma mistura difícil, mas como você pode ver acima, muito disso é principalmente o código Angular e não muito Firestore. O Firebase Firestore pode fazer consultas muito mais complexas, mas para fins de demonstração, eu mantive-o simples. Em teoria, se sua estrutura de dados permanecer a mesma, você pode trocar o Firestore e colocar em diferentes conjuntos de conexões API com o mínimo de refatoração necessária.

Palavras Finais

Eu pessoalmente gosto do Firebase por causa de como é fácil criar rapidamente projetos prontos para produção sem a necessidade de criar um backend inteiro e um servidor para apoiá-lo. Custo razoável, não é muito ruim e os projetos de teste são livres para inicializar (e eles não fazem você digitar os detalhes do seu cartão de crédito até que você esteja pronto para trocar de planos). No geral, levou mais tempo para criar o formulário e a interface em torno do aplicativo do que o ato real de conectar o aplicativo até o Firestore. Eu não adicionei validação de formulário a este tutorial devido à extensão, mas vou criar outro post sobre isso no futuro.

Deixe um comentário