Imprimir

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>

Consulta de datos a partir de un ID

firestore.service.ts

public consultarPorId(coleccion, documentId) {
  return this.angularFirestore.collection(coleccion).doc(documentId).snapshotChanges();
}

xxxx.page.ts

Lo habitual puede ser que la consulta de los datos de un determinado documento por su ID se haga en un página distinta, por lo que no especifica aquí el nombre de la página donde colocar este código.

Los datos (un documento de FireStore) que se obtengan tras realizar la consulta se deben almacenar en alguna variable con una estructura que permita diferenciar el ID y los datos (DATA) obtenidos. En este ejemplo se va a crear la variable document con los campos id y data (de tipo Tarea):

document: any = {
  id: "",
  data: {} as Tarea
};

En el lugar del código que corresponda (donde se desee realizar la consulta por ID), se incluirán las siguientes líneas que realizan la consulta buscando el ID que se encuentre almacenado en la variable idConsultar (no se incluye en este ejemplo su declaración y asignación de valor, ya que depende del modo de uso que se haga de este tipo de consulta).

this.firestoreService.consultarPorId("tareas", idConsultar).subscribe((resultado) => {
  // Preguntar si se hay encontrado un document con ese ID
  if(resultado.payload.data() != null) {
    this.document.id = resultado.payload.id
    this.document.data = resultado.payload.data();
    // Como ejemplo, mostrar el título de la tarea en consola
    console.log(this.document.data.titulo);
  } else {
    // No se ha encontrado un document con ese ID. Vaciar los datos que hubiera
    this.document.data = {} as Tarea;
  } 
});

xxxx.page.html

Para mostrar algún dato contenido en la consulta se deberá incluir algo como esto (muestra el título de la tarea consultada):

<p> Título: {{ document.data.titulo }} </p>