Como criar CRUD com Angular e Firebase Realtime Database – Em alguns simples passos

Nessa vídeo aula eu mostro como criar um CRUD (CRUD significa, em inglês, Create, Read, Update e Delete e em português é Incluir, Alterar, Excluir e Consultar) com Angular e Firebase Realtime Database.

O que é o Firebase Realtime Database

O Firebase Realtime Database é um banco de dados NoSQL hospedado na nuvem. Com ele, você armazena e sincroniza dados entre os seus usuários em tempo real.

Para saber mais você pode acessar diretamente a página do Firebase clicando aqui.

Pré requisitos

Ter um projeto já criado no console do Firebase e com as permissões de leitura e escrita habilitadas. (Isso é mostrado no vídeo)

Esse tutorial está utilizando as seguintes versões das dependências:

  • Angular: 7.0
  • Firebase: 5.5.5
  • AngularFire2: 5.1.0

Passo a passo para o CRUD

  1. Instalar o Firebase no projeto
  2. Criar os componentes, services e classes
  3. Criar a classe que vai ter os dados do contato
  4. Criar o service que vai compartilhar o contato entre os dois componentes
  5. Criar o service que fará o CRUD
  6. Criar o formulário de cadastro
  7. Criar a lista de contatos

1 – Instalar o Firebase no projeto

Para fazer a integração do Firebase com o Angular é necessário usar uma biblioteca desenvolvida pelo próprio time do Angular que se chama AngularFire2.

Para instalar, basta rodar o comando abaixo que vai instalar a exata versão do Firebase e AngularFire2 usada nesse tutorial

npm install firebase@5.5.5 @angular/fire@5.1.0 --save

Próximo passo é adicionar as configurações do Firebase no arquivo environment.ts. Essas configurações podem ser obtidas na página do projeto.

firebase: {
	apiKey: "[SUA KEY]",
	authDomain: "[SEU AUTH DOMAIN]",
	databaseURL: "[SUA DATABASE URL]",
	projectId: "[SEU PROJECT ID]",
	storageBucket: "[SEU STORAGE BUCKET]",
	messagingSenderId: "[SUA MESSAGING SENDER ID]"
}

Feito isso, agora é necessário importar no app.module os módulos do AngularFire2 que serão usados. Nesse tutorial será usado apenas o modulo do Firebase Realtime Database.

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { FormsModule }   from '@angular/forms';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

import { AngularFireModule } from '@angular/fire';
import { AngularFireDatabaseModule } from '@angular/fire/database';
import { environment } from '../environments/environment';
import { EditComponent } from './contatos/edit/edit.component';
import { ListComponent } from './contatos/list/list.component';

@NgModule({
  declarations: [
    AppComponent,
    EditComponent,
    ListComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    AppRoutingModule,
    AngularFireModule.initializeApp(environment.firebase),
    AngularFireDatabaseModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

2 – Criar os componentes, services e classes

O projeto será um CRUD de contatos com nome e telefone, para isso é necessário criar um componente para o formulário e outro para a lista de contatos. Alem disso será criado o service que fará o crud no Firebase.
Basta rodar os comandos abaixo para a criação dos componentes, services e classes.

ng g component contatos/edit
ng g component contatos/list
ng g service contatos/shared/contato
ng g service contatos/shared/contato-data
ng g class contatos/shared/contato

3 – Classe com os dados do contato

O arquivo contato.ts deve ficar com o conteúdo abaixo.

export class Contato {
  nome: string = '';
  telefone: string = '';
}

4 – Service para compartilhar o contato entre os componentes

Durante a alteração de um contato, é necessário enviar o contato que está sendo editado para o componente de formulário.
Para isso o o arquivo contato-data.service.ts deve ficar com o conteúdo abaixo.
Como o foco do tutorial é mostrar o CRUD, esse arquivo não será explicado aqui mas ele é explicado no vídeo.

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { Contato } from './contato';

@Injectable({
  providedIn: 'root'
})
export class ContatoDataService {
  private contatoSource = new BehaviorSubject({ contato: null, key: '' });
  currentContato = this.contatoSource.asObservable();

  constructor() { }

  changeContato(contato: Contato, key: string) {
    this.contatoSource.next({ contato: contato, key: key });
  }
}

5 – Service responsável pelo CRUD.

O service tem 4 métodos: Inclusão, alteração, consulta e exclusão.

Para incluir um dado no Firebase é necessário acessar o nó onde os dados serão salvos e executar o método push passando como parâmetro o dado que deve ser incluído, conforme abaixo.

Método para incluir um registro

insert(contato: Contato) {
  this.db.list('contato').push(contato)
    .then((result: any) => {
      console.log(result.key);
    });
}

Para alterar um dado no Firebase, é necessário acessar o nó onde os dados serão salvos e executar o método update passando como parâmetro a key do objeto e o dados que deve ser alterado, conforme abaixo.

update(contato: Contato, key: string) {
  this.db.list('contato').update(key, contato)
    .catch((error: any) => {
      console.error(error);
    });
}

Para buscar uma lista de dados no Firebase, é necessário acessar o nó onde os dados estão e mapear da maneira que você quer acessar as propriedades desse objeto para mostrar na tela, conforme abaixo.

getAll() {
  return this.db.list('contato')
    .snapshotChanges()
    .pipe(
      map(changes => {
        return changes.map(c => ({ key: c.payload.key, ...c.payload.val() }));
      })
    );
}

E por fim, para excluir, basta acessar o nó do objeto e executar o método remove, conforme abaixo.

delete(key: string) {
  this.db.object(`contato/${key}`).remove();
}

O arquivo contato.service.ts com todos os métodos explicados acima deve ficar conforme abaixo.

import { Injectable } from '@angular/core';
import { AngularFireDatabase } from '@angular/fire/database';
import { Contato } from './contato';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class ContatoService {

  constructor(private db: AngularFireDatabase) { }

  insert(contato: Contato) {
    this.db.list('contato').push(contato)
      .then((result: any) => {
        console.log(result.key);
      });
  }

  update(contato: Contato, key: string) {
    this.db.list('contato').update(key, contato)
      .catch((error: any) => {
        console.error(error);
      });
  }

  getAll() {
    return this.db.list('contato')
      .snapshotChanges()
      .pipe(
        map(changes => {
          return changes.map(c => ({ key: c.payload.key, ...c.payload.val() }));
        })
      );
  }

  delete(key: string) {
    this.db.object(`contato/${key}`).remove();
  }
}

6 – Formulário de cadastro

Para salvar os contatos no Firebase o formulário criado nesse tutorial fica conforme abaixo

edit.component.ts

import { Component, OnInit, Input } from '@angular/core';
import { Contato } from '../shared/contato';
import { ContatoService } from '../shared/contato.service';
import { ContatoDataService } from '../shared/contato-data.service';

@Component({
  selector: 'app-edit',
  templateUrl: './edit.component.html',
  styleUrls: ['./edit.component.scss']
})
export class EditComponent implements OnInit {
  contato: Contato
  key: string = '';

  constructor(private contatoService: ContatoService, private contatoDataService: ContatoDataService) { }

  ngOnInit() {
    this.contato = new Contato();
    this.contatoDataService.currentContato.subscribe(data => {
      if (data.contato && data.key) {
        this.contato = new Contato();
        this.contato.nome = data.contato.nome;
        this.contato.telefone = data.contato.telefone;
        this.key = data.key;
      }
    })
  }

  onSubmit() {
    if (this.key) {
      this.contatoService.update(this.contato, this.key);
    } else {
      this.contatoService.insert(this.contato);
    }

    this.contato = new Contato();
  }
}

edit.component.html

<form (ngSubmit)="onSubmit()">

  <div class="form-group">
    <label>Nome</label>
    <input type="text" class="form-control" [(ngModel)]="contato.nome" name="nome" />
  </div>

  <div class="form-group">
    <label>Telefone</label>
    <input type="tel" class="form-control" [(ngModel)]="contato.telefone" name="telefone" />
  </div>

  <button type="submit" class="btn btn-primary">Salvar</button>
</form>

7 – Listagem de contato

Para exibir os contatos cadastrados na tela, o componente criado nesse tutorial fica conforme abaixo.
list.component.ts

import { Contato } from './../shared/contato';
import { Component, OnInit } from '@angular/core';
import { ContatoService } from '../shared/contato.service';
import { Observable } from 'rxjs';
import { ContatoDataService } from '../shared/contato-data.service';

@Component({
  selector: 'app-list',
  templateUrl: './list.component.html',
  styleUrls: ['./list.component.scss']
})
export class ListComponent implements OnInit {
  contatos: Observable<any>;

  constructor(private contatoService: ContatoService, private contatoDataService: ContatoDataService) { }

  ngOnInit() {
    this.contatos = this.contatoService.getAll();
  }

  delete(key: string) {
    this.contatoService.delete(key);
  }

  edit(contato: Contato, key: string) {
    this.contatoDataService.changeContato(contato, key);
  }
}

list.component.html

<div class="card mt-2" *ngFor="let contato of contatos | async">
  <div class="card-body">
    <h5 class="card-title">{{contato.nome}}</h5>
    <p class="card-text">{{contato.telefone}}</p>
  </div>
  <div class="card-footer">
    <button type="button" class="btn btn-default" (click)="edit(contato, contato.key)">Editar</button>
    <button type="button" class="btn btn-danger ml-2" (click)="delete(contato.key)">Excluir</button>
  </div>
</div>

 

Referencias