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.

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.
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">×</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.
Crear y agregar un proyecto en firebase
Vamos a la pagina oficial de firebase y creamos un nuevo proyecto.

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.

Si quieres conocer otros artículos parecidos a CRUD Angular y Firebase completo desde cero con paginación en tabla puedes visitar la categoría Programación.
Entradas relacionadas