Cómo CRUD en Angular + Firebase Firestore

CRUD – Crear, Leer, Actualizar, Borrar – el cuatro santo grial de las acciones de base de datos. Son las únicas cosas que necesitarás para hacer algo significativo con tu base de datos. Claro, usted puede aumentar la complejidad de las consultas, pero al final del día, todo se reduce a estas cuatro acciones.

Firebase es un sistema basado en la nube propiedad de Google que viene completo con ganchos API, espacio de almacenamiento de archivos, sistema de autenticación y capacidades de alojamiento. Es un sistema muy infravalorado que debería utilizarse más para la creación de prototipos y el desarrollo rápido de aplicaciones.

Si quieres arrancar una aplicación web progresiva pero no tienes la experiencia de backend para configurar servidores, crear APIs y lidiar con bases de datos, entonces Firebase es una opción fantástica para los desarrolladores de front-end que pueden sentirse aislados y empantanados por la enorme colina de información que tienen que procesar incluso para poner su aplicación en marcha.

O si simplemente tienes poco tiempo, Firebase puede reducir tus horas de desarrollo casi a la mitad para que puedas centrarte en la experiencia del usuario y en la implementación de esos flujos de UI. También es lo suficientemente flexible como para migrar tu aplicación front-end y utilizar una base de datos diferente si es necesario.

Aquí tienes una guía rápida sobre cómo implementar acciones CRUD con Angular y Firebase.

Es una aplicación de pedidos de café en la que puedes añadir pedidos (crear), listar pedidos de la base de datos (leer), marcar un pedido como completado (actualizar) y eliminar un pedido (eliminar).

El propósito de este tutorial es ayudarte a empezar con Firebase Firestore y ver lo fácil que es conectarse y empezar con el servicio propiedad de Google. Esto no es un anuncio para Google (no recibo ninguna comisión de ellos por esto) sino simplemente una ilustración de cómo Angular juega con la base de datos.

La aplicación que vamos a hacer no es perfecta. Faltan cosas como la validación de datos y un millón de posibles características que también puedo añadir. Pero ese no es el punto. El punto es configurar en Angular tan rápido como sea posible y conseguir que funcione con una base de datos en vivo.

Así que, basta de la introducción – aquí está el paseo por el código.

La configuración inicial

Configura tu aplicación Angular a través de la CLI de Angular. Si aún no tienes Angular CLI, puedes encontrar más información sobre él aquí.

En resumen, simplemente ejecuta estos comandos en tu terminal en el directorio donde quieres que se encuentre tu app de Angular. Aquí están los comandos y lo que hacen.

npm install -g @angular/cli

Instala Angular CLI en tu terminal si no lo tienes ya.

ng new name-of-app-here

Esto creará un nuevo proyecto Angular usando la última versión de Angular disponible.

cd name-of-app-here

Cuando creas un nuevo proyecto a través de Angular CLI, creará una nueva carpeta de proyecto para ti. cd te llevará a esa carpeta a través de la terminal.

ng serve

Esto es iniciará y ejecutará tu proyecto Angular.

Configurando Angular

Vamos a crear 3 nuevas partes en total para esta app Angular – pedidos, lista de pedidos y shared/ordersService. Los dos primeros son componentes que contendrán la interfaz de la app y el servicio shared/orders que mantendrá todas las llamadas a la API de Firebase juntas.

Para crear los archivos necesarios, ejecuta los siguientes comandos:

ng g c orders

g significa generar y c significa componente. La última parte del comando es el nombre de su archivo, que para el caso anterior se llama órdenes. También puede utilizar ng generate component orders para lograr el mismo efecto.

Cree otro componente para la lista de pedidos utilizando el siguiente comando.

ng g c order-list

Y finalmente, para el servicio, utilice s en lugar de c como a continuación:

ng g s shared/orders

Esto creará un archivo orders.service.ts en una carpeta llamada shared. Asegúrese de añadir orders.service.ts en app.module.ts porque esto no se hace por usted automáticamente como los componentes. Puedes hacerlo a través de import y añadiéndolo a la lista providers así:

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

El CSS

Para este tutorial, usaremos Materialize CSS para que nuestra aplicación final se vea mejor que el css por defecto. No es necesario y puedes usar cualquier framework CSS o tu propio CSS personalizado si quieres. Puedes consultar los detalles de Materialize CSS aquí.

También vamos a utilizar los iconos de material de Googles como botones para marcar el pedido de café como completado o eliminar el pedido.

Una forma de implementar esto es tener el código de abajo justo encima de la etiqueta </head> en su archivo index.html ubicado en la carpeta 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" />

El código de la vista inicial de Angular

En app.component.html, eliminar todo el código de inicio. Para ello usaremos nuestro propio contenido.

Nos engancharemos al componente que acabamos de crear y los mostraremos en pantalla usando los selectores app-orders y app-order-list. Para ello, escribimos el siguiente código:

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

Las clases container, row, col y s6 forman parte del sistema de rejilla CSS de Materialize. Todas las clases que verás en el resto de este tutorial son de Materialize CSS, a menos que se mencione lo contrario.

Configuración del formulario

Para configurar los formularios, importa ReactiveFormsModule en app.module.ts y recuerda importarlo en la matriz imports.

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

Dentro de orders.service.ts importamos FormControl y FormGroup de @angular/formsy creamos un nuevo formulario fuera de constructor donde podemos configurar las propiedades del formulario como se indica a continuación:

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)
})
}

Estos valores los utilizaremos para almacenarlos en Firebase un poco más adelante en este tutorial.

Usando el formulario dentro de un componente

import la clase OrdersService en su componente para que pueda utilizar el objeto dentro de su componente y luego crear un objeto de la clase dentro de la constructor.

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

Dentro de su orders.component.html utilice la directiva formGroup para hacer referencia al objeto formulario creado en OrdersService. Cada formControlName hace referencia a los nombres que utilizamos en la formGroup creada dentro de la clase OrdersService. Esto permitirá que el controlador asociado utilice las variables escritas en el formulario. Ver el código de abajo:

<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>

En el orders.component.ts, vamos a establecer una matriz de café para el bucle a través de nuestro orders.component.html. En teoría, también podría configurar esto dentro de su base de datos Firestore, hacer una llamada a la colección y luego usarlo. Pero para propósitos de longitud, vamos a configurarlo como una matriz local. Dentro de su clase OrdersComponent, configure la siguiente matriz:

coffees = ;

En su orders.component.html y dentro de sus etiquetas <form>, haga un bucle a través de ella usando *ngFor con un manejador de acción (click) para agregar nuevos cafés a su orden. Vamos a mostrar la lista de pedidos justo debajo con la posibilidad de eliminar un café individual de la siguiente manera:

<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 creas un array vacío para albergar el pedido de cafés, utilizas la función addCoffee() para añadir nuevos cafés y removeCoffee() para eliminar una bebida de tu 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);
};

Manejo del envío del formulario

Añade una entrada Submit Order dentro y al final de las etiquetas <form> y añade el onSubmit() a un manejador de clics como el siguiente:

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

Crea una función onSubmit vacía en tu orders.component para el intermedio. Pronto añadiremos el manejo de eventos. En este punto, su formulario debería estar listo para conectarse a su base de datos Firebase.

Configuración del componente de listado

Ahora sólo necesitamos un espacio para mostrar nuestros pedidos de café desde la base de datos. Por ahora, sólo vamos a configurar el andamio html.

Navega hasta tu order-list.component.html y crea una tabla con 3 encabezados y 5 celdas de datos. Las 3 primeras celdas de datos contendrán los valores extraídos de la base de datos y las 2 últimas contendrán una funcionalidad extra que te permitirá marcar el pedido como completo o eliminarlo.

Configuración de Firebase

Ve a tu consola de Firebase y añade un nuevo proyecto.

Haz clic en ‘Añadir proyecto’ para crear un nuevo proyecto.

Añade un nombre de proyecto, acepta los términos y condiciones y haz clic en crear proyecto.

Da un nombre a tu proyecto y acepta los términos de controlador-controlador para crear un proyecto.

Cuando tu proyecto Firebase haya sido configurado, verás algo así.

Crea una base de datos seleccionando Base de datos en el panel lateral (situado bajo Desarrollar) y luego haz clic en Crear base de datos bajo el banner de Cloud Firestore.

Haz clic en ‘Crear base de datos’

Selecciona iniciar en modo de prueba para las reglas de seguridad. Puede modificarlo más tarde.

Recuerde mantener sus credenciales en su máquina local sólo por motivos de seguridad.

Obtendrá una base de datos Firestore vacía como esta.

Conectando Firebase Firestore a Angular

Para conectar tu aplicación Angular a Firebase, vas a necesitar instalar los paquetes firebase y @angular/fire. Esto te dará acceso a AngularFireModule y AngularFirestoreModule. Utiliza el siguiente comando en tu terminal para instalarlos.

npm i --save firebase @angular/fire

Implementación de los conectores

Vuelve a tu consola web de Firebase y coge los detalles de configuración para utilizarlos en tu app de Angular. Esto se encuentra en su página de descripción del proyecto. Se parece a esto:

Tendrás que usar tus propias credenciales. La configuración anterior no le funcionará.

Copie la sección resaltada del código, navegue a su archivo environment.ts (ubicado dentro de la carpeta environments) y pegue los detalles de la configuración dentro del objeto environment como firebaseConfig. Vea el ejemplo de abajo:

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 los paquetes que instaló antes y el archivo environment.ts en su app.module.ts.Necesitará inicializar el AngularFireModule con el firebaseConfig que acaba de configurar. Véase el ejemplo siguiente:

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

Configuración de Firestore en un servicio

Importa AngularFirestore en tu archivo orders.service.ts y decláralo en el constructor para que tu servicio lo conozca.

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

Ahora ya tienes todo listo para empezar con la parte CRUD de este tutorial de Angular + Firebase Firestore.

C de Create

Para crear un nuevo registro en tu flamante base de datos Firestore, vas a necesitar llamar a .add() . Vamos a hacer esto dentro de la orders.service.ts archivo.

Para hacer esto, usted tendrá que especificar el collection nombre y lo que los datos que desea empujar a la base de datos. En nuestro caso, es coffeeOrders.

El código de ejemplo que se muestra a continuación utiliza una promesa para devolver la llamada a Firebase y luego te permite decidir qué quieres hacer después de que todo haya terminado.

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

Para llamar a esta función en tu componente, navega a orders.component.ts y dentro de la función onSubmit() y el manejador de acción, haz una llamada al createCoffeeOrder() a través de ordersService.

El ejemplo de abajo hace algo de procesamiento de los datos mapeando el array coffeeOrder al valor del formulario coffeeOrder. También he creado una variable data para propósitos de desestructuración (junto con no tener que pasar un nombre masivamente largo en 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*/
});
}
...

¡Y viola! Ahora has creado un registro desde tu aplicación Angular localhost a tu base de datos Firestore.

R es de Read

Para mostrar los datos de tus pedidos de café, vas a necesitar una función de lectura en tu orders.service.ts. El siguiente ejemplo de código le dará todos los valores almacenados en su colección coffeeOrders.

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

Para utilizar esta función, tendrá que llamarla desde su orders-list.component.ts . Puedes hacer esto importando el OrdersService en el archivo e inicializarlo en el constructor.

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

Crea una función que contenga tu llamada e inicialízala en el ngOnInit() para llamarla cuando la vista se cargue por primera vez. Cree una variable coffeeOrders para asignar los resultados devueltos de su base de datos a través de subscribe(). Usaremos esto para iterar y mostrar en order-list.component.html

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

Para usar coffeeOrders en tu order-list.component.html , usa *ngFor para hacer un bucle a través del array devuelto. También necesita hacer un poco de incepción y hacer otro bucle para la parte coffeeOrder para obtener su lista de cafés para cada cliente. Hay maneras más eficientes de hacer esto, pero para este tutorial, ver el código de ejemplo a continuación:

...
<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>
...

Y ahí lo tienes. Ahora has conectado las capacidades de lectura a tu aplicación Angular.

U es de Update

Digamos que queremos ser capaces de marcar el pedido de café como completo en la base de datos y hacer algo a la partida basada en el cambio. Debido a que snapshot() mantiene un seguimiento de cualquier cambio que sucede, no es necesario hacer ningún seguimiento o sondeo a la base de datos.

Vamos a crear otra celda de datos en nuestra tabla con un icono de ‘check’ de Google Materialize Icons y engancharlo a un evento (click) que llamará a la función markCompleted() en su order-list.component.ts. También vamos a pasar en el orden particular para este bucle.

Vamos a establecer un atributo con el valor completed a la celda de datos para que pueda determinar dinámicamente si queremos tener el icono de ‘check’ en la pantalla. Hemos establecido originalmente este valor como false cuando creamos por primera vez el formulario en orders.service.ts .

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

Inside order-list.component.ts , crear la función markCompleted() que utiliza el data inyectado para pasar a una función llamada updateCoffeeOrder() en orders.service.ts.

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

Inside orders.service.ts crear la función de manejo. Esta función se conectará y llamará a su base de datos Firestore basándose en la colección seleccionada y el id del documento. Ya sabemos que nuestra colección se llama coffeeOrders y podemos encontrar el id del documento basado en los parámetros pasados en la llamada a la función del componente.

.set() establecerá el registro específico con los datos que usted pasó. .set() toma dos parámetros – sus datos y un objeto de configuración. Si usas merge: true, significa que sólo actualizas el par valor-clave pasado en lugar de reemplazar todo el documento con lo que pasaste.

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

Ahora, cuando hagas clic en el icono de ‘check’ en tu vista, se actualizará tu base de datos y desaparecerá porque tu atributo está ahora establecido en true.

D es de Borrar

Para la acción final, vamos a configurar todo de manera similar al proceso de actualización – pero en lugar de actualizar el registro, vamos a eliminarlo.

Configura otra celda de datos con un controlador de eventos de clic que llama a una función deleteOrder(). Vamos a pasar en la instancia de los datos order en el bucle cuando se hace clic en el icono ‘delete_forever’. Necesitarás esto para el id del documento. Ver el código de abajo para el ejemplo:

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

Dentro de su order-list.component.ts crear la función asociada que llama a una función deleteCoffeeOrder() dentro de su orders.service.ts .

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

Dentro de su archivo orders.service.ts, crear la función deleteCoffeeOrder() y utilizar el data inyectado para averiguar cuál es el id del documento. Al igual que en el caso de la actualización, necesitas conocer tanto el nombre de la colección como el id del documento para identificar correctamente qué registro quieres eliminar. Usa .delete() para decirle a Firestore que quieres eliminar el registro.

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

Ahora, cuando hagas clic en el icono ‘delete_forever’, tu aplicación Angular se disparará y le dirá a Firestore que elimine el registro específico. Tu registro desaparecerá de la vista cuando se elimine el documento especificado.

Aplicación final

Puedes encontrar el repositorio de Github para todo el proyecto de trabajo aquí. Tendrás que crear tu propia base de datos Firebase y conectarla tú mismo actualizando los archivos de configuración. El código en sí no es perfecto pero lo he mantenido muy mínimo para que puedas ver cómo funciona Firestore en una aplicación Angular sin tener que pasar por una jungla de código.

He intentado condensar el tutorial lo máximo posible sin perder ningún detalle. Fue una mezcla difícil, pero como se puede ver arriba, gran parte es sobre todo el código de Angular y no mucho Firestore. Firebase Firestore puede hacer consultas mucho más complejas pero para fines de demostración, lo he mantenido simple. En teoría, si tu estructura de datos sigue siendo la misma, puedes cambiar Firestore y poner un conjunto diferente de conexiones API con una refactorización mínima requerida.

Palabras finales

Personalmente me gusta Firebase por lo fácil que es crear rápidamente proyectos listos para la producción sin la necesidad de crear todo un backend y un servidor para soportarlo. En cuanto al coste, no está tan mal y los proyectos de prueba son gratuitos para arrancar (y no te hacen introducir los datos de tu tarjeta de crédito hasta que estás listo para cambiar de plan). En general, me llevó más tiempo crear el formulario y la interfaz que rodea la aplicación que el acto real de conectar la aplicación a Firestore. No he añadido la validación de formularios a este tutorial debido a la longitud pero crearé otro post sobre ello en el futuro.

Deja un comentario