CRUD Angular y Firebase completo desde cero con paginación en tabla

En este post te explicare como crear un CRUD usando Angular y Firebase. Todo este tutorial sera desde cero, es decir, tratare de explicar lo más claro posible, así como al final de este post te dejare un video-tutorial con todo el proceso paso a paso, pero si eso no es suficiente, también te dejare el link del repositorio en github para que puedas clonar el proyecto.

Nota: este tutorial se ha realizado con Angular versión 9 y posteriormente lo he actualizado a la versión 10.

Vamos a crear el típico sistema para administrar estudiantes. Lo atractivo, es que vamos a crear una tabla con paginación.

Tabla de contenido
  1. CRUD Angular y Firebase desde cero
    1. Crear proyecto en angular
    2. Estructura del proyecto
    3. Agregar Firebase al proyecto
    4. Agregar ng-bootstrap al proyecto
    5. Agregar ngx pagination
    6. App module
    7. Crear service
    8. App component html
    9. App component ts
    10. Crear y agregar un proyecto en firebase
  2. Vídeo-Tutorial en español
  3. Repositorio código fuente

CRUD Angular y Firebase desde cero

Lo primero que vamos a hacer es dejar claro en concepto de CRUD. En realidad CRUD hace referencia a las operaciones basicas que debe tener una plataforma o aplicación, estas operaciones son:

  • C: Create
  • R: Read
  • U: Update
  • D: Delete

Crear proyecto en angular

Para crear un proyecto en Angular ejecutamos el siguiente código en tu consola preferida.

ng new tutorialfirebase --routing

El parámetro --routing es para agregar el router al proyecto, aunque para este tutorial no lo vamos a necesitar, así que es opcional.

Estructura del proyecto

Al finalizar este tutorial, nuestro proyecto tendrá la siguiente estructura.

Agregar Firebase al proyecto

Ahora vamos a agregar la dependencia de Firebase a nuestro proyecto.

npm i --save firebase @angular/fire

Una vez hemos agregado las dependencias anteriores, podemos crear los archivos necesarios para el funcionamiento de nuestro proyecto.

Recomendado:   Google Adsense en medio de las entradas o post en Blogger

Agregar ng-bootstrap al proyecto

Para este proyecto usaremos algunos componentes de ng-bootstrap, asi que vamos a agregar la siguiente dependencia.

ng add @ng-bootstrap/ng-bootstrap

Esto es para crear un modal más o menos parecido a esto, el cual se usara para agregar los nuevos estudiantes.

Leer documentación completa aquí.

Agregar ngx pagination

Esta dependencia la usaremos para administrar la paginación de la tabla en caso de que existan muchos elementos para mostrar.

npm install ngx-pagination --save

Leer documentación completa aquí.

App module

Como ya tenemos todas las dependencias instaladas, nuestro app.module.ts quedaría asi:

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

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NgxPaginationModule } from 'ngx-pagination';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { ReactiveFormsModule, FormsModule  } from '@angular/forms';
import { AngularFireModule } from '@angular/fire';
import { environment } from '../environments/environment';

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

Crear service

Necesitaremos crear un service para poder hacer la comunicación entre Firebase y nuestro proyecto.

ng g s services/firebaseService

Ahí estamos creando un servicio dentro de una carpeta llamada services.

Nuestro archivo firebase-service.service.ts quedaria asi:

import { Injectable } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';

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

  constructor(
    private firestore: AngularFirestore
  ) { }

  /**
   * Metodo para listar todos los estudiantes
   */
  getEstudiantes(){
    return this.firestore.collection("estudiantes").snapshotChanges();
  }

  /**
   * crea un estudiante en firebase
   * @param estudiante estudiante a crear
   */
  createEstudiante(estudiante:any){
    return this.firestore.collection("estudiantes").add(estudiante);
  }

  /**
   * actualiza un estudiante existente en firebase
   * @param id id de la coleccion en firebase
   * @param estudiante estudiante a actualizar
   */
  updateEstudiante(id:any, estudiante:any){
    return this.firestore.collection("estudiantes").doc(id).update(estudiante);
  }


  /**
   * borrar un estudiante existente en firebase
   * @param id id de la coleccion en firebase
   */
  deleteEstudiante(id:any){
    return this.firestore.collection("estudiantes").doc(id).delete();

  }
}

App component html

Ahora veamos como queda nuestro archivo app.component.html

<div style="text-align: center;">
  <h2>Sistema de estudiantes</h2>
  <button type="button" class="btn btn-success" (click)="open(content)">Crear nuevo estudiante</button>
  <table class="table">
    <thead>
      <tr>
        <th>ID</th>
        <th>Nombre</th>
        <th>Apellido</th>
        <th>Opciones</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let item of collection.data | paginate: config">
        <td>{{item.id}}</td>
        <td>{{item.nombre}}</td>
        <td>{{item.apellido}}</td>
        <td>
          <button type="button" class="btn btn-primary" (click)="openEditar(content,item)">Editar</button>
          <button type="button" class="btn btn-danger" (click)="eliminar(item)">Eliminar</button>
        </td>
      </tr>
    </tbody>
  </table>
  <pagination-controls (pageChange)="pageChanged($event)"></pagination-controls>
  <ng-template #content let-modal>
    <div class="modal-header">
      <h4 class="modal-title" id="modal-basic-title">Estudiante create/update</h4>
      <button type="button" class="close" aria-label="Close" (click)="modal.dismiss('Cross click')">
        <span aria-hidden="true">&times;</span>
      </button>
    </div>
    <div class="modal-body">
      <form>
        <div class="form-group">
          <form [formGroup]="estudianteForm">
            <div class="form-group">
              <label>Id</label>
              <input type="number" formControlName="id" class="form-control">
            </div>
            <div class="form-group">
              <label>Nombre</label>
              <input type="text" formControlName="nombre" class="form-control">
            </div>
            <div class="form-group">
              <label>Apellido</label>
              <input type="text" formControlName="apellido" class="form-control">
            </div>
          </form>
        </div>
      </form>
    </div>
    <div class="modal-footer">
      <button type="button" class="btn btn-outline-dark" (click)="guardarEstudiante()" *ngIf="!actualizar">Guardar</button>
      <button type="button" class="btn btn-outline-dark" (click)="actualizarEstudiante()" *ngIf="actualizar">Actualizar</button>
    </div>
  </ng-template>
</div>

App component ts

Así es como queda nuestro archivo app.component.ts el cual es el encargado de toda la lógica.

import { Component, OnInit } from '@angular/core';
import { NgbModal, ModalDismissReasons } from '@ng-bootstrap/ng-bootstrap';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { FirebaseServiceService } from './services/firebase-service.service';
import { isNullOrUndefined } from 'util';

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

  closeResult = '';

  estudianteForm: FormGroup;

  idFirabaseActualizar: string;
  actualizar: boolean;


  constructor(
    private modalService: NgbModal,
    public fb: FormBuilder,
    private firebaseServiceService: FirebaseServiceService
  ) { }

  config: any;
  collection = { count: 0, data: [] }

  ngOnInit(): void {
    this.idFirabaseActualizar = "";
    this.actualizar = false;
    //configuracion para la paginación
    this.config = {
      itemsPerPage: 5,
      currentPage: 1,
      totalItems: this.collection.data.length
    };
    //inicializando formulario para guardar los estudiantes
    this.estudianteForm = this.fb.group({
      id: ['', Validators.required],
      nombre: ['', Validators.required],
      apellido: ['', Validators.required],
    });
    //cargando todos los estudiantes de firebase
    this.firebaseServiceService.getEstudiantes().subscribe(resp => {
      this.collection.data = resp.map((e: any) => {
        return {
          id: e.payload.doc.data().id,
          nombre: e.payload.doc.data().nombre,
          apellido: e.payload.doc.data().apellido,
          idFirebase: e.payload.doc.id
        }
      })
    },
      error => {
        console.error(error);
      }
    );
  }


  pageChanged(event) {
    this.config.currentPage = event;
  }

  eliminar(item: any): void {
    this.firebaseServiceService.deleteEstudiante(item.idFirebase);
  }

  guardarEstudiante(): void {
    this.firebaseServiceService.createEstudiante(this.estudianteForm.value).then(resp => {
      this.estudianteForm.reset();
      this.modalService.dismissAll();
    }).catch(error => {
      console.error(error)
    })
  }

  actualizarEstudiante() {
    if (!isNullOrUndefined(this.idFirabaseActualizar)) {
      this.firebaseServiceService.updateEstudiante(this.idFirabaseActualizar, this.estudianteForm.value).then(resp => {
        this.estudianteForm.reset();
        this.modalService.dismissAll();
      }).catch(error => {
        console.error(error);
      });
    }
  }


  openEditar(content, item: any) {

    //llenar form para editar
    this.estudianteForm.setValue({
      id: item.id,
      nombre: item.nombre,
      apellido: item.apellido,
    });
    this.idFirabaseActualizar = item.idFirebase;
    this.actualizar = true;
    this.modalService.open(content, { ariaLabelledBy: 'modal-basic-title' }).result.then((result) => {
      this.closeResult = `Closed with: ${result}`;
    }, (reason) => {
      this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
    });
  }

  open(content) {
    this.actualizar = false;
    this.modalService.open(content, { ariaLabelledBy: 'modal-basic-title' }).result.then((result) => {
      this.closeResult = `Closed with: ${result}`;
    }, (reason) => {
      this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
    });
  }

  private getDismissReason(reason: any): string {
    if (reason === ModalDismissReasons.ESC) {
      return 'by pressing ESC';
    } else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
      return 'by clicking on a backdrop';
    } else {
      return `with: ${reason}`;
    }
  }

}

Y para finalizar, lo único que nos resta es conectarnos a firebase.

Recomendado:   Gratis libro recomendado para aprender a programar en C#

Crear y agregar un proyecto en firebase

Vamos a la pagina oficial de firebase y creamos un nuevo proyecto.

como crear un nuevo proyecto en firebase

Ahora vamos a la sección de opciones.

Y agregamos nuestra aplicación web, es decir, nuestro proyecto en angular.

Ahora vamos a copiar esos parametros de configuracion en nuestro archivo de enviorments.

En los dos archivos debe quedar algo parecido, obviamente deben usar sus propias credenciales generadas por Firebase.

Conclusiones

Durante este tutorial hemos aprendido a crear una aplicación web con Angular 9, la cual tiene las operaciones básicas de CRUD usando Firebase.

Y este es el modal flotante para crear o actualizar un nuevo estudiante.

Vídeo-Tutorial en español

En el siguiente video te explico paso a paso como fue que programé el CRUD de Angular + Firebase.

Repositorio código fuente

Si tienes algunas dudas o simplemente quieres descargar el codigo fuente completo, puedes ir al siguiente repositorio.

Una vez dentro, simplemente lo clonas (si sabes usar github) o sino, puedes descargarlo como zip y asi obtendras tu proyecto CRUD en Angular y Firebase.

descargar proyecto CRUD angular con Firebase desde cero.

Subir