Configuración de la base de datos en Google Cloud Firestore

Accede a la consola de Google Cloud Firestore desde la dirección: console.firebase.google.com

Captura de pantalla 2019 02 08 a las 13.42.55 c1de7

Una vez que hayas creado la base de datos, accede a la opción Database en el menú de la parte izquierda, abre la pestaña Reglas y activa inicialmente la opción de modo de prueba para facilitar la lectura y escritura en la base de datos sin controlar de momento qué usuarios pueden hacerlo (posteriormente se debería establecer un control más exhaustivo).

Captura de pantalla 2019 02 08 a las 13.43.09 230d6

En la pestaña Datos podrás ir viendo el contenido de la base de datos en cada momento.

Captura de pantalla 2019 02 08 a las 13.45.11 b837c

Creación del nuevo proyecto Ionic

Ejecuta las siguientes sentencias en el terminal para crear un nuevo proyecto en blanco de Ionic, e instalar el módulo de firebase que permite el acceso a la base de datos remota de Google.

ionic start ejemplo-firestore blank
cd ejemplo-firestore 
npm install firebase @angular/fire

Configurar el acceso a la base de datos en el proyecto

Entra en la sección Project Overview y haz clic en el botón </> para obtener la información necesaria para configurar el servicio en una aplicación web o también para Ionic.

Captura de pantalla 2019 02 08 a las 13.51.44 e04fb 3540c

Copia los valores de los atributos apiKey, authDomain, ...  dentro del archivo environment.ts como se muestra a continuación:

Captura de pantalla 2019 11 03 a las 22.09.13 af587 

environments/environment.ts

export const environment = {
  production: false,
  firebase: {
    apiKey: "????????????",
    authDomain: "????????????",
    databaseURL: "????????????",
    projectId: "????????????",
    storageBucket: "????????????",
    messagingSenderId: "????????????",
appId: "????????????" }
};

 app.module.ts

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

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

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

import { AngularFireModule } from '@angular/fire';
import { AngularFirestoreModule } from '@angular/fire/firestore';
import { environment } from '../environments/environment';

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [BrowserModule, 
    IonicModule.forRoot(), 
    AppRoutingModule,
    AngularFireModule.initializeApp(environment.firebase),
    AngularFirestoreModule],
  providers: [
    StatusBar,
    SplashScreen,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

Interface para declarar el tipo de objeto a almacenar en la base de datos

ionic generate interface Tarea

tarea.ts

export interface Tarea {
    titulo: string;
    descripcion: string;
} 

Clase para métodos de acceso a la base de datos

Para organizar mejor el código vamos a crear una nueva clase dentro de los servicios del proyecto. Se le va a asignar como nombre "firestore", por lo que se creará el archivo firestore.service.ts dentro de la carpeta app del proyecto.

ionic generate service firestore

Insertar registros

firestore.service.ts

import { Injectable } from '@angular/core';

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

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

  constructor(private angularFirestore: AngularFirestore) { 
} public insertar(coleccion, datos) { return this.angularFirestore.collection(coleccion).add(datos); }   }

home.page.html

<ion-header>
  <ion-toolbar>
    <ion-title>
      Ejemplo Firestore
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content padding>
  <ion-item>
    <ion-label>Título</ion-label>
    <ion-input [(ngModel)]="tareaEditando.titulo"></ion-input>
  </ion-item>
  <ion-item>
    <ion-label>Descripción</ion-label>
    <ion-input [(ngModel)]="tareaEditando.descripcion"></ion-input>
  </ion-item>
  <ion-button (click)="clicBotonInsertar()">Añadir tarea</ion-button>
</ion-content>

home.page.ts

import { Component } from '@angular/core';
import { FirestoreService } from '../firestore.service';
import { Tarea } from '../tarea';
@Component({ selector: 'app-home', templateUrl: 'home.page.html', styleUrls: ['home.page.scss'], }) export class HomePage { tareaEditando: Tarea; constructor(private firestoreService: FirestoreService) { // Crear una tarea vacía this.tareaEditando = {} as Tarea; } clicBotonInsertar() { this.firestoreService.insertar("tareas", this.tareaEditando).then(() => { console.log('Tarea creada correctamente!'); this.tareaEditando= {} as Tarea; }, (error) => { console.error(error); }); } }

Captura de pantalla 2019 02 15 a las 12.53.55 57b80

Captura de pantalla 2019 02 15 a las 12.56.14 29d92

Obtener lista de registros

firestore.service.ts

  public consultar(coleccion) {
    return this.angularFirestore.collection(coleccion).snapshotChanges();
  }

home.page.ts

  arrayColeccionTareas: any = [{
    id: "",
    data: {} as Tarea
   }];

  constructor(private firestoreService: FirestoreService) {
    this.obtenerListaTareas();
  }

  obtenerListaTareas(){
    this.firestoreService.consultar("tareas").subscribe((resultadoConsultaTareas) => {
      this.arrayColeccionTareas = [];
      resultadoConsultaTareas.forEach((datosTarea: any) => {
        this.arrayColeccionTareas.push({
          id: datosTarea.payload.doc.id,
          data: datosTarea.payload.doc.data()
        });
      })
    });
  }

home.page.html

  <h1>LISTA DE TAREAS</h1>
  <ion-list>
    <ion-item *ngFor="let documentTarea of arrayColeccionTareas">
      <ion-grid>
        <ion-row>
          <h2>{{documentTarea.data.titulo}}</h2>
        </ion-row>
        <ion-row>
          <p>{{documentTarea.data.descripcion}}</p>
        </ion-row>
      </ion-grid>
    </ion-item>
  </ion-list>

Borrado de datos

firestore.service.ts

 public borrar(coleccion, documentId) {
return this.angularFirestore.collection(coleccion).doc(documentId).delete();
}

home.page.ts

  idTareaSelec: string;

  selecTarea(tareaSelec) {
    console.log("Tarea seleccionada: ");
    console.log(tareaSelec);
    this.idTareaSelec = tareaSelec.id;
    this.tareaEditando.titulo = tareaSelec.data.titulo;
    this.tareaEditando.descripcion = tareaSelec.data.descripcion;
  }

  clicBotonBorrar() {
    this.firestoreService.borrar("tareas", this.idTareaSelec).then(() => {
      // Actualizar la lista completa
      this.obtenerListaTareas();
      // Limpiar datos de pantalla
      this.tareaEditando = {} as Tarea;
    })
  }

home.page.html

Añadir a cada item de la lista la llamada al método selecTarea cuando se seleccione un determinado item.

  <ion-list>
    <ion-item *ngFor="let documentTarea of arrayColeccionTareas" (click)="selecTarea(documentTarea)">
      ...
    </ion-item>
  </ion-list>

En este ejemplo se ha añadido un botón para eliminar el item que se encuentre seleccionado: 

  <ion-button (click)="clicBotonBorrar()">Borrar tarea</ion-button>

Modificación de datos

firestore.service.ts

public actualizar(coleccion, documentId, datos) {
return this.angularFirestore.collection(coleccion).doc(documentId).set(datos);
}

home.page.ts

  clicBotonModificar() {
    this.firestoreService.actualizar("tareas", this.idTareaSelec, this.tareaEditando).then(() => {
      // Actualizar la lista completa
      this.obtenerListaTareas();
      // Limpiar datos de pantalla
      this.tareaEditando = {} as Tarea;
    })
  }

home.page.html

El código de este botón debe incluirse junto con la llamada al método selecTarea() que ya se ha mostrado anteriormente al seleccionar un item de la lista.

<ion-button (click)="clicBotonModificar()">Modificar tarea</ion-button>