Archivo de la etiqueta: Android Android

Tutorial de Ionic – Peticiones http – API REST

En el siguiente enlace tienes el índice para acceder al resto de entradas de este tutorial:

 

¡¡Atención!! este tutorial se basa en ionic 3 y está desactualizado por lo que es posible que los ejemplos no funcionen en la última versión de ionic, haz click aquí para acceder a un tutorial mas actual de Ionic.

 

Hola a todos,  hoy vamos a ver como podemos comunicar una aplicación desarrollada con ionic con una API REST, para ello vamos a aprender como realizar peticiones a un servidor remoto a través de http.

En esta pequeña prueba vamos a ver como podemos acceder a una API REST para obtener desde un servidor remoto un listado de usuarios. Para este pequeño ejemplo vamos a utilizar RANDOM USER GENERATOR que como se indica en su web es una API libre de código abierto para generar datos de usuario aleatorios para realizar pruebas. Como Lorem Ipsum, pero con personas.

Lo que vamos a hacer es simplemente realizar una llamada a esta API donde recibiremos como respuesta un listado de usuarios que mostraremos en nuestra vista.

Antes de nada vamos a crear una nueva aplicación de prueba:

ionic start pruebahttp1 blank

Ahora vamos a crear un provider donde gestionaremos la comunicación con el servidor remoto:

ionic g provider http

Se habrá creado una carpeta providers (si no existía) y dentro una carpeta con el nombre del provider que acabamos de crear (http) y dentro un archivo .ts con el mismo nombre.

Por defecto contendrá el siguiente código:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

/*
  Generated class for the HttpProvider provider.

  See https://angular.io/guide/dependency-injection for more info on providers
  and Angular DI.
*/
@Injectable()
export class HttpProvider {

  constructor(public http: HttpClient) {
    console.log('Hello HttpProvider Provider');
  }

}

Vemos que al crear un provider ya se importa el módulo HttpClient.

Para poder utilizar HttpClient tenemos que importar y declarar HttpClientModule en el archivo app.module.ts:

import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';

import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { HttpProvider } from '../providers/http/http';
import { HttpClientModule } from '@angular/common/http';



@NgModule({
  declarations: [
    MyApp,
    HomePage
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
    HttpClientModule
    
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler},
    HttpProvider
  ]
})
export class AppModule {}

http.get

Ahora vamos a añadir un método que llamaremos loadUsers a nuestro provider para obtener la lista de usuarios desde el servidor, por lo tanto editamos el archivo http.ts y añadimos el siguiente código:
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

/*
  Generated class for the HttpProvider provider.

  See https://angular.io/guide/dependency-injection for more info on providers
  and Angular DI.
*/
@Injectable()
export class HttpProvider {

  path : string = 'https://randomuser.me/api/?results=25';
  
  constructor(public http: HttpClient) {
    console.log('Hello HttpProvider Provider');
  }

  loadUsers(){
    return this.http
    .get(this.path)
  }
}
http.get devuelve el resultado de la solicitud en la forma de un observable.
El listado de usuarios tendrá un formato similar a este:
{
  "results": [
    {
      "gender": "male",
      "name": {
        "title": "mr",
        "first": "romain",
        "last": "hoogmoed"
      },
      "location": {
        "street": "1861 jan pieterszoon coenstraat",
        "city": "maasdriel",
        "state": "zeeland",
        "postcode": 69217
      },
      "email": "romain.hoogmoed@example.com",
      "login": {
        "username": "lazyduck408",
        "password": "jokers",
        "salt": "UGtRFz4N",
        "md5": "6d83a8c084731ee73eb5f9398b923183",
        "sha1": "cb21097d8c430f2716538e365447910d90476f6e",
        "sha256": "5a9b09c86195b8d8b01ee219d7d9794e2abb6641a2351850c49c309f1fc204a0"
      },
      "dob": "1983-07-14 07:29:45",
      "registered": "2010-09-24 02:10:42",
      "phone": "(656)-976-4980",
      "cell": "(065)-247-9303",
      "id": {
        "name": "BSN",
        "value": "04242023"
      },
      "picture": {
        "large": "https://randomuser.me/api/portraits/men/83.jpg",
        "medium": "https://randomuser.me/api/portraits/med/men/83.jpg",
        "thumbnail": "https://randomuser.me/api/portraits/thumb/men/83.jpg"
      },
      "nat": "NL"
    }
  ],
  "info": {
    "seed": "2da87e9305069f1d",
    "results": 1,
    "page": 1,
    "version": "1.1"
  }
}
Ahora vamos a crear la vista en home.html para mostrar un botón que llamará a la función
cargarUsuarios, y un listado de items con los usuarios que crearemos recorriendo con *ngFor el array usuarios que posteriormente vamos a crear en el controlador:
<ion-header>
  <ion-navbar>
    <ion-title>
      Usuarios
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content>
  <ion-list>
    <ion-item *ngFor="let usuario of usuarios">
      <ion-avatar item-start>
        <img [src]="usuario.picture.medium">
      </ion-avatar>
      <h2>{{ usuario.name.first }}</h2>
      <p>{{ usuario.email }}</p>
    </ion-item>
  </ion-list>
  <button ion-button full (click) = "cargarUsuarios()">Cargar Usuarios</button>
</ion-content>
Ahora vamos a modificar home.ts para obtener los datos desde el provider y mostrarlos en la vista.
Editamos home.ts e importamos el provider httpProvider que acabamos de crear:
import { HttpProvider } from '../../providers/http/http';
Para poder utilizarlo debemos inyectarlo en el constructor:
constructor(public navCtrl: NavController, public http: HttpProvider) {

  }
Justo antes del constructor vamos a definir una variable miembro donde guardaremos el array de usuarios que recibamos desde el servidor:
usuarios : any[];

Promesas y Observables

Ahora vamos a crear un método que que a su vez llamará al método loadUsers de nuestro provider para recibir los datos de los usuarios:
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { HttpProvider } from '../../providers/http/http';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  usuarios : any[];

  constructor(public navCtrl: NavController, public http: HttpProvider) {

  }

  cargarUsuarios(){
    this.http.loadUsers().subscribe(
      (res) => { 
        this.usuarios = res['results'];
      },
      (error) =>{
        console.error(error);
      }
    )
  }


}

Como podemos observar llamamos al método loadUsers que hemos definido en el provider, pero no utilizamos then sino subscribe, esto es porque http no devuelve una promesa si no que devuelve un observable. Un observable se queda a la espera de recibir datos y nosotros nos “subscribimos” recibiendo estos datos en cuanto estén disponibles.
Esta cualidad se puede utilizar para suscribirnos a la url y observar si ha habido cambios, como por ejemplo si es un sitio de noticias donde se están continuamente renovando.

La diferencia entre promesas y observables a groso modo es que la promesa devuelve los datos una única vez cuando estos son recibidos mientras que un observable se queda “vigilando” si se han producido cambios y se ejecuta cada vez que un cambio se produce, aunque hasta que no te suscribes a un observable éste no se ejecutará.

Si solo necesitamos recibir los datos una única vez sin necesidad de observar si se han producido cambios podemos utilizar promesas. Una promesa se ejecuta una vez que se haya resuelto la llamada y recibimos los datos en la función .then(res=>{… }).

El código del provider en el archivo http.ts utilizando una promesa quedaría de la siguiente manera:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import 'rxjs/add/operator/toPromise';

/*
  Generated class for the HttpProvider provider.

  See https://angular.io/guide/dependency-injection for more info on providers
  and Angular DI.
*/
@Injectable()
export class HttpProvider {

  path : string = 'https://randomuser.me/api/?results=25';
  
  constructor(public http: HttpClient) {
    console.log('Hello HttpProvider Provider');
  }

  loadUsers(){
    return this.http
    .get(this.path)
    .toPromise();

  }  
}

Debemos importar el operador toPromise para convertir el observable en promesa  con import ‘rxjs/add/operator/toPromise’;  y simplemente añadir toPromise() después del get.

Ahora en home.ts solo tenemos que sustituir subscribe por then:

cargarUsuarios(){
  this.http.loadUsers().then(
    (res) => { 
      this.usuarios = res['results'];
    },
    (error) =>{
      console.error(error);
    }
  )
}

Si ejecutáis la aplicación con ionic serve -l y pulsáis en el botón Cargar Usuarios podréis ver algo como esto:

Listado de usuarios
Listado de usuarios

Podéis observar como cada vez que pulséis el botón la lista de usuarios cambia ya que Random User Generator como su propio nombre indica devuelve una lista aleatoria de usuarios.

http.post

RANDOM USER GENERATOR  nos ha servido para aprender ha hacer una petición get a una API REST para recibir datos, en este caso una lista de usuarios.

Si queréis conectaros con un servicio que tengáis corriendo en un servidor remoto y necesitáis pasarle datos desde la aplicación para que se guarden en el servidor tenemos que usar http.post.

Vamos a imaginar que tenemos corriendo un servicio en PHP cuya url sea www.miservicio.com/adduser/ que está programado para recibir vía post un nuevo usuario para guardarlo en la base de datos del servidor.

Tendremos que crear  una función similar a esta:

postDatos(){
  let datos = { nombre:'Edu',email:'edu.revilla.vaquero@gmail.com'}
  
  let options = {
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  };
 var url = 'www.miservicio.com/adduser/';
 return new Promise(resolve => {
  this.http.post(url,JSON.stringify(datos),options)
     .subscribe(data => {
       resolve(data);
      });
 });
}

En la variable datos  tenemos un objeto con los datos del usuario que queremos enviar al servidor, en este caso nombre y email.

Después definimos la variable options donde a su vez definimos las cabeceras en la variable headers, dependiendo de la configuración del servidor podría necesitar parámetros diferentes en la cabecera.

Como vemos http.post es bastante parecido a http.get, solo que como segundo parámetro le pasamos la variable que contiene los datos que queremos enviar convertida a formato JSON con JSON.stringify(datos), como tercer parámetro le pasamos options.

En data recibiremos la respuesta que nos de el servidor.

Vamos a ver un ejemplo de como obtendríamos los datos enviados desde nuestra aplicación con un servicio desarrollado en PHP en el servidor:

<?php
$postdata = file_get_contents("php://input");
if (isset($postdata)) {
$request = json_decode($postdata);
echo $request->nombre;
echo $request->email;
}
?>

Evidentemente en otros lenguajes de programación del lado del servidor el código para recibir los datos sería diferente, pero se escapa del propósito de este tutorial el abordar como sería en cada leguaje. Si alguno hace la prueba en otro lenguaje y quiere compartirlo en los comentarios para ayudar a los demás será bien recibido :-).

Eso es todo por hoy.

Si necesitas desarrollar una aplicación móvil no dudes en solicitarme un presupuesto sin compromiso:

Tutorial de Ionic – Crear una aplicación para guardar nuestros sitios geolocalizados – Parte 8 – Eliminar un sitio desde el listado deslizando el item con "ItemSliding".

En el siguiente enlace tienes el índice para acceder al resto de entradas de este tutorial:

 

¡¡Atención!! este tutorial se basa en ionic 3 y está desactualizado por lo que es posible que los ejemplos no funcionen en la última versión de ionic, haz click aquí para acceder a un tutorial mas actual de Ionic.

Hola a todos.

En el post anterior  vimos como modificar un sitio, hoy vamos a mejorar nuestra app para que se puedan eliminar nuestros sitio, pero para aprender más sobre las posibilidades de ionic lo vamos ha hacer desde el listado, deslizando el elemento del listado hacia la izquierda nos mostrará el botón de eliminar. También aprovecharemos para aprender a utilizar AlertController para mostrar un dialogo que pida confirmación antes de eliminar el sitio.

Vamos a ver como sería:

Lo primero que vamos a hacer es modificar el listado para añadir un ion-item-sliding que es un componente que nos permite deslizar el item, debemos introducir el item dentro de ion-item-sliding y le vamos a añadir despues del item un elemento ion-item-options que nos permite añadir opciones que se mostrarán al deslizar el item. Se pueden añadir tantas opciones como desees, nosotros solo vamos a necesitar una para borrar el elemento. Vamos a ver como tiene que quedar el código, editamos el archivo listado.html y lo dejamos de la siguiente manera:

<!--
  Generated template for the ListadoPage page.

  See http://ionicframework.com/docs/components/#navigation for more info on
  Ionic pages and navigation.
-->
<ion-header>

  <ion-navbar>
    <ion-title>Listado</ion-title>
  </ion-navbar>

</ion-header>


<ion-content padding>
<ion-list>
  <ion-item-sliding #item *ngFor="let sitio of sitios">
    <ion-item (click)="muestraSitio(sitio)">
      <ion-thumbnail item-left>
        <img [src]="sitio.foto">
      </ion-thumbnail>
      <h2>{{ sitio.address }}</h2>
      <p>{{ sitio.description }}</p>
    </ion-item>
    <ion-item-options side="right">
      <button ion-button  color="danger" (click)="borrarSitio(sitio.id)"><ion-icon name="trash"></ion-icon>
         Borrar
      </button>
    </ion-item-options>
  </ion-item-sliding>
</ion-list>
</ion-content>

Como podemos observar hemos puesto el *ngFor en la etiqueta ion-item-sliding que ahora envuelve al ion-item que ya teníamos.

Despues del ion-item hemos añadido un componente ion-item-options al que le indicamos que se muestre en el lado derecho (side=”right”).

Dentro de ion-item-options tenemos un botón con in icono (ion-icon) que muestra el icono “trash” que es el típico cubo de basura y el texto “borrar”.
En el evento click del botón le decimos que llame a la función borrarSitio.

Si probamos la aplicación podéis comprobar en el listado que los items ahora se pueden arrastrar hacia la izquierda y aparece el botón de borrar:

Borrando un sitio
Borrando un sitio

Ahora debemos definir la función borrar sitio en el controlador del listado. Antes de borrar vamos a sacar un dialogo de confirmación que nos preguntará si realmente queremos eliminar el sitio, para ello vamos a utilizar el componente Alert.

Alerts

Los alerts son una excelente manera de ofrecer al usuario la posibilidad de elegir una acción específica o una lista de acciones. También pueden proporcionar al usuario información importante, o requerir que tomen una decisión.

Ionic nos proporciona los siguientes tipos de Alerts:

En este caso  como lo que queremos es que el usuario confirme el borrado del sitio vamos a utilizar alert de confirmación.

Para poder utilizar un alert lo primero que debemos hacer es importar AlertController e inyectarlo en el constructor, por lo tanto vamos a editar el archivo listado.ts y añadimos el siguiente código:

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams, ModalController } from 'ionic-angular';
import { DbProvider } from '../../providers/db/db';
import { AlertController } from 'ionic-angular';


/**
 * Generated class for the ListadoPage page.
 *
 * See http://ionicframework.com/docs/components/#navigation for more info
 * on Ionic pages and navigation.
 */
@IonicPage()
@Component({
  selector: 'page-listado',
  templateUrl: 'listado.html',
})
export class ListadoPage {

  sitios: any;
  constructor(public navCtrl: NavController,
              public navParams: NavParams,
              public modalCtrl : ModalController,
              public db : DbProvider,
              public alertCtrl : AlertController) {
  }

....

Ahora  vamos a  a crear la función borrarSitio() y definiremos dentro de ella el alert de la siguiente manera:

borrarSitio(id){

    let alert = this.alertCtrl.create({
      title: 'Confirmar borrado',
      message: '¿Estás seguro de que deseas eliminar este sitio?',
      buttons: [
        {
          text: 'No',
          role: 'cancel',
          handler: () => {
            // Ha respondido que no así que no hacemos nada
          }
        },
        {
          text: 'Si',
          handler: () => {
               // AquÍ borramos el sitio en la base de datos

           }
        }
      ]
    });

    alert.present();

 }

Para crear un alert utilizamos el método create donde tenemos que definir el título en este caso “Confirmar borrado”, el mensaje: “¿Estas seguro de que deseas eliminar este sitio?”.

Después definimos los dos botones de acción uno para SI y otro para NO, en text definimos el texto del botón y en handler definiremos la acción que debemos realizar, en caso de pulsar “No” no hacemos nada, en caso de pulsar “Si” procederemos a eliminar el sitio.

Una vez definido el alert con alert.present() hacemos que se muestre.

Ahora que ya sabemos como se usan los alerts vamos a escribir el código del botón “Si” para borrar el sitio.

Lo primero que tenemos que hacer es crear un nuevo método en el provider DbProvider para eliminar los sitios, por lo tanto editamos el archivo db.ts y añadimos la función borrarSitio():

public borrarSitio(id){
     let sql = "DELETE FROM sitios WHERE id= ? ";
     return this.db.executeSql(sql,[id]);
  }

Recibimos como parámetro id que es el id del sitio que queremos eliminar de la base de datos, ejecutamos la query y si todo ha ido bien e sitio se habrá eliminado para siempre de la base de datos.

Ahora que ya tenemos preparada la función para borrar el sitio en el provider vamos a volver al alert de confirmación en listado.ts para definir las operaciones a realizar al pulsar el botón “Si”, en la función handler del botón “Si” añadimos el siguiente código:

borrarSitio(id){

    let alert = this.alertCtrl.create({
      title: 'Confirmar borrado',
      message: '¿Estás seguro de que deseas eliminar este sitio?',
      buttons: [
        {
          text: 'No',
          role: 'cancel',
          handler: () => {
              // Ha respondido que no así que no hacemos nada         
           }
        },
        {
          text: 'Si',
          handler: () => {

              this.db.borrarSitio(id).then((res)=>{
            // Una vez borrado el sitio recargamos el listado
              this.db.getSitios().then((res)=>{
              this.sitios = [];
              for(var i = 0; i < res.rows.length; i++){
                  this.sitios.push({
                    id : res.rows.item(i).id,
                    lat: res.rows.item(i).lat,
                    lng: res.rows.item(i).lng,
                    address: res.rows.item(i).address,
                    description: res.rows.item(i).description,
                    foto: res.rows.item(i).foto
                  });
              }

              },(err)=>{ /* alert('error al sacar de la bd'+err) */ })

            },(err)=>{ /* alert('error al borrar de la bd'+err) */ });
          }
        }
      ]
    });

    alert.present();

   }

Lo primero que hacemos es llamar al método que acabamos de crear en el provider con this.db.borrarSitio(id) pasándole como parámetro el id del sitio.

En el then lo que vamos ha hacer es refrescar el listado para ello debemos modificar la variable miembro this.sitios que contiene los sitios que se muestran en el listado, lo que hacemos es volver a cargar de la base de datos los sitios, como acabamos de borrar un sitio este no aparecerá. Para cargar los sitios de la base de datos utilizamos el método this.db.getSitios que ya tenemos definido y hacemos exactamente el mismo proceso que en la función  ionViewDidEnter para rellenar el array this.sitios con los datos obtenidos desde la base de datos. De hecho podemos definir una función que se encargue de recoger los sitios de la base de datos y rellenar el array this.sitios en lugar de repetir el código de ionViewDidEnter, eso ya lo dejo como mejora opcional.

Si ejecutáis el código comprobaréis que ya podéis borrar los sitio que no os interesen conservar en vuestro dispositivo.

Eso es todo por hoy.

 

Si necesitas desarrollar una aplicación móvil no dudes en solicitarme un presupuesto sin compromiso:

Tutorial de Ionic – Crear una aplicación para guardar nuestros sitios geolocalizados – Parte 5 – Guardando nuestros sitios en una base de datos local

En el siguiente enlace tienes el índice para acceder al resto de entradas de este tutorial:

 

¡¡Atención!! este tutorial se basa en ionic 3 y está desactualizado por lo que es posible que los ejemplos no funcionen en la última versión de ionic, haz click aquí para acceder a un tutorial mas actual de Ionic.

Hola a todos:

En el post anterior obtuvimos la dirección a partir de las coordenadas, creamos el formulario para introducir la descripción del lugar y aprendimos a sacar fotografías con nuestro móvil, bien, todo esto nos vale de muy poco si cuando cerramos la aplicación perdemos toda esta información.

Para poder guardar nuestros sitios y que sigan allí para poder consultarlos siempre que queramos tenemos que almacenarlos en una base de datos local en el dispositivo.

Provider

Antes de nada vamos a introducir un concepto nuevo, para manejar los datos que introduciremos y extraeremos de la base de datos vamos a utilizar un provider.

Los providers son proveedores que se encargan del manejo de datos, bien extraídos de la base de datos, desde una API REST, etc.

Para crear un provider acudimos una vez más a ionic generator con el comando  ionic g provider.

Para crear un proveedor para gestionar nuestra base de datos de sitios escribimos desde consola:

ionic g provider db

Esto nos creará un archivo typescript con el nombre del proveedor que hemos creado, en este caso db.ts dentro de la carpeta providers.

Por supuesto podéis dar el nombre que deseéis al provider que acabamos que crear, no tiene por que ser  “db”.

Vamos a echar un vistazo al código que se ha generado por defecto en db.ts:

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';

/*
  Generated class for the Db provider.

  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
  for more info on providers and Angular 2 DI.
*/
@Injectable()
export class Db {

  constructor(public http: Http) {
    console.log('Hello Db Provider');
  }

}

Por defecto al crear un provider importa Http ya que los providers pueden ser utilizados para obtener datos des una petición a un servicio mediante Http, sin embargo como lo vamos a utilizar para gestionar nuestra base de datos no lo necesitamos y podemos eliminarlo, también debemos eliminarlo del constructor. Tampoco vamos a necesitar el “import ‘rxjs/add/operator/map'”, por lo que podemos eliminarlo también.

Para crear una base de datos vamos a utilizar SQlite, así que lo primero que necesitamos es instalar el plugin escribiendo desde consola:

ionic cordova plugin add cordova-sqlite-storage
npm install --save @ionic-native/sqlite

Recuerda que si está utilizando Linux o Mac necesitarás utilizar sudo por delante.

Una vez instalado el plugin ya podemos importarlo  en nuestro provider desde ionic-native.
Para poder utilizarlo  inyectamos la dependencia SQLite en el constructor, además vamos a crear una variable miembro llamada db donde se guardará el manejador de la base de datos una vez establecida la conexión :

import { Injectable } from '@angular/core';
import { SQLite, SQLiteObject } from '@ionic-native/sqlite';

/*
  Generated class for the Db provider.

  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
  for more info on providers and Angular 2 DI.
*/
@Injectable()
export class DbProvider {

  db : SQLiteObject = null;
  constructor(public sqlite: SQLite) {
    console.log('Hello Db Provider');
  }

...
Ahora vamos a crear en nuestro provider un método para crear/abrir la base de datos:
public openDb(){
      return this.sqlite.create({
          name: 'data.db',
          location: 'default' // el campo location es obligatorio
      })
      .then((db: SQLiteObject) => {
       this.db =db;
     })
  }

Como vemos utilizamos el método openDatabase al cual le pasamos como parámetro un objeto especificando el nombre y la localización.El campo location lo dejamos en ‘default’.

Este método crea la base de datos si no existe y abre la conexión.
Si todo ha ido bien resuelve la promesa pasandonos como parámetro el manejador de la base de datos que es de tipo SQLiteObject, se lo asignamos a this.db para poder utilizarlo en las funciones que vamos a crear.

Ahora vamos a crear una tabla con los campos que vamos a necesitar para guardar nuestros sitios con los campos que necesitamos:

public createTableSitios(){
    return this.db.executeSql("create table if not exists sitios( id INTEGER PRIMARY KEY AUTOINCREMENT, lat FLOAT, lng FLOAT, address TEXT, description TEXT, foto TEXT )",{})
  }

Si ya has trabajado antes con otras bases de datos como por ejemplo MySQL  no te costará trabajo entender como funciona SQLite ya que la sintaxis es muy similar aunque con algunas limitaciones.

En este caso nuestra tabla va a tener un campo id de tipo integer que sera la clave primaria, un campo lat de tipo float donde guardaremos la latitud de las coordenadas, un campo lng de tipo float donde guardaremos la longitud, un campo address de tipo text para guardar la dirección, un campo llamado description de tipo text donde guardaremos la descripción del sitio y por último un campo foto también de tipo text donde guardaremos la foto en formato base 64.

Para continuar vamos a crear un método para guardar nuestros sitios:

public addSitio(sitio){
    let sql = "INSERT INTO sitios (lat, lng, address, description, foto) values (?,?,?,?,?)";
    return this.db.executeSql(sql,[sitio.lat,sitio.lng,sitio.address,sitio.description,sitio.foto]);
  }

En la sql definimos el insert con los campos de la tabla que vamos a introducir y en los valores ponemos interrogaciones ‘?’,  luego en el método execute pasamos como primer parámetro la sql y como segundo un array con los valores que corresponden a los campos donde van las interrogaciones, es decir hay que poner el valor de los campos en el mismo orden. El valor de los campos lo recibimos como parámetro en el objeto sitio. Cuando posteriormente llamemos a la función addSitio le tendremos que pasar un objeto con todos los campos.

Para poder utilizar el provider  y SQLite debemos importar ambos en app.module.ts y declararlos en la sección providers, ionic generator nos ha añadido automáticamente nuestro provider DbProvider en app.module.ts así que solo tenemos que preocuparnos de importar y declarar Sqlite:

import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';

import { MyApp } from './app.component';

import { Geolocation } from '@ionic-native/geolocation';
import { Camera } from '@ionic-native/camera';
import { DbProvider } from '../providers/db/db';
import { SQLite } from '@ionic-native/sqlite';


@NgModule({
  declarations: [
    MyApp
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler},
    Geolocation,
    Camera,
    DbProvider,
    SQLite
  ]
})
export class AppModule {}

Vamos a importar nuestro provider también en app.component.ts y vamos a abrir la base de datos y crear la tabla en platform.ready para asegurarnos de que el plugin SQlite ya se ha cargado antes de utilizarlo:

import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { DbProvider } from '../providers/db/db';

@Component({
  templateUrl: 'app.html'
})
export class MyApp {
  rootPage:any = 'MisTabsPage';

  constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen, public db: DbProvider) {
    platform.ready().then(() => {
      // Okay, so the platform is ready and our plugins are available.
      // Here you can do any higher level native things you might need.
      statusBar.styleDefault();
      splashScreen.hide();
        this.db.openDb()
       .then(() => this.db.createTableSitios())

    });
  }
}

Por último importamos el provider en la página donde vamos a utilizarlo, en este caso en modal-nuevo-sitio.ts, también debemos inyectarlo en el constructor :

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams, ViewController } from 'ionic-angular';
import { Camera, CameraOptions } from '@ionic-native/camera';
import { DbProvider } from '../../providers/db/db';



/**
 * Generated class for the ModalNuevoSitio page.
 *
 * See http://ionicframework.com/docs/components/#navigation for more info
 * on Ionic pages and navigation.
 */
@IonicPage()
@Component({
  selector: 'page-modal-nuevo-sitio',
  templateUrl: 'modal-nuevo-sitio.html',
})
export class ModalNuevoSitioPage {

  coords : any = { lat: 0, lng: 0 }
  address: string;
  description: string = '';
  foto: any = '';

  constructor(public navCtrl: NavController, public navParams: NavParams, private viewCtrl : ViewController, private camera: Camera,  private db: DbProvider) {
  }

...

Si recordamos el post anterior, en la vista  modal-nuevo-sitio.html, en el evento ngSubmit del formulario llamábamos a la función guardarSitio().
Vamos a definir esta función en el controlador del modal en el archivo modal-nuevo-sitio.ts:

guardarSitio(){
    let sitio = {
      lat: this.coords.lat,
      lng: this.coords.lng ,
      address: this.address,
      description: this.description,
      foto: this.foto
    }
    this.db.addSitio(sitio).then((res)=>{
      this.cerrarModal();
     /*  alert('se ha introducido correctamente en la bd'); */
    },(err)=>{ /* alert('error al meter en la bd'+err) */ })
}

Bien, como podemos observar en el código lo que hacemos es crear un objeto llamado sitio al que le asignamos los valores que tenemos recogidos.

Recuerda que el valor de la descripción  se refleja automáticamente el la variable this.description  que tenemos definida en el controlador al utilizar [(ngModel)], las coordenadas y la dirección ya las teníamos recogidas y la foto se le asigna a this.foto en el momento de sacarla.

Después llamamos al método addSitio que hemos definido en nuestro provider pasándole como parámetro el objeto sitio.

Si todo ha ido bien cerramos el modal.

Bien, en este punto ya podemos guardar nuestros sitios en la base de datos, ahora nos toca poder sacar y mostrar un listado de los sitios que hemos guardado.

Para ello lo primero que vamos a hacer es crear un nuevo método en nuestro provider para obtener los sitios que tenemos guardados en la base de datos, por lo tanto editamos el archivo db.ts y añadimos el método getSitios:

import { Injectable } from '@angular/core';
import { SQLite, SQLiteObject } from '@ionic-native/sqlite';

/*
  Generated class for the Db provider.

  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
  for more info on providers and Angular 2 DI.
*/
@Injectable()
export class Db {

  db : SQLiteObject = null;
  constructor(public sqlite: SQLite) {
    console.log('Hello Db Provider');
  }

  public openDb(){
      return this.sqlite.create({
          name: 'data.db',
          location: 'default' // el campo location es obligatorio
      })
      .then((db: SQLiteObject) => {
       this.db =db;
     })
  }

  public createTableSitios(){
    return this.db.executeSql("create table if not exists sitios( id INTEGER PRIMARY KEY AUTOINCREMENT, lat FLOAT, lng FLOAT, address TEXT, description TEXT, foto TEXT )",{})
  }

  public addSitio(sitio){
    let sql = "INSERT INTO sitios (lat, lng, address, description, foto) values (?,?,?,?,?)";
    return this.db.executeSql(sql,[sitio.lat,sitio.lng,sitio.address,sitio.description,sitio.foto]);
  }

  public getSitios(){
    let sql = "SELECT * FROM sitios";
    return this.db.executeSql(sql,{});
  }

}

Ha llegado el momento de mostrar nuestros sitios en la página listado, así que editamos el archivo listado.ts y vamos a importar el provider DbProvider en el controlador de la página listado, debemos una vez más inyectar DbProvider en el constructor, también vamos a crear una variable miembro llamada sitios  de tipo any que contendrá un array con todos los sitios que tenemos guardados:

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { DbProvider } from '../../providers/db/db';


/**
 * Generated class for the Listado page.
 *
 * See http://ionicframework.com/docs/components/#navigation for more info
 * on Ionic pages and navigation.
 */

@IonicPage()
@Component({
  selector: 'page-listado',
  templateUrl: 'listado.html',
})
export class Listado {
  sitios: any;

  constructor(public navCtrl: NavController, public navParams: NavParams,public db : DbProvider) {
  }

...

Ahora necesitamos extraer nuestros sitios de la base de datos cuando accedamos a la página del listado, podríamos hacerlo en el evento ionViewDidLoad que se ejecuta al cargar la página, el problema es que al contrario del modal, que se carga cada vez que lo llamamos, las paginas que se muestran al cambiar de pestaña se cargan una única vez al inicio, por lo que si guardamos nuevos sitios estos no se refrescaran a no ser que cierres la aplicación y la vuelvas a abrir.

Este es por lo tanto un buen momento para incorporar un nuevo concepto:

Ciclo de vida de una página

Cuando generamos una página con ionic generator vemos que nos crea por defecto el método ionViewDidLoad().

Como ya hemos comentado este método se ejecuta cuando la página se ha cargado:

ionViewDidLoad() {
    console.log('ionViewDidLoad ListadoPage');
 }

Vamos a ver los métodos con los que cuenta ionic en función de los eventos del ciclo de vida de una página:

  • ionViewDidLoad:
    Se ejecuta cuando la página se ha terminado de cargar.
       Este evento sólo ocurre una vez por página cuando se está creando.
       
  • ionViewWillEnter
       Se ejecuta cuando la página está a punto de entrar y convertirse en la página activa.

  • ionViewDidEnter:
       Se ejecuta cuando la página ha sido cargada y ahora es la página activa.
      
  • ionViewWillLeave:
       Se ejecuta cuando se está a punto de salir de la página y ya no será la página activa.

  • ionViewDidLeave:
       Se ejecuta cuando se ha salido de forma completa de la página
       y ya no es la página activa.

  • ionViewWillUnload:
       Se ejecuta cuando la página está a punto de ser destruida,
       ella y todos sus elementos.

Bien, una vez sabido esto, vamos a cargar nuestros sitios en el evento ionViewDidEnter, es decir cada vez que entremos en la página, una vez se ha cargado y está activa.

Añadimos el método ionViewDidEnter al controlador de la página listado en el archivo listado.ts y hacemos la llamada para extraer nuestros sitios de la base de datos:

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { DbProvider } from '../../providers/db/db';


/**
 * Generated class for the Listado page.
 *
 * See http://ionicframework.com/docs/components/#navigation for more info
 * on Ionic pages and navigation.
 */
@IonicPage()
@Component({
  selector: 'page-listado',
  templateUrl: 'listado.html',
})
export class ListadoPage {
  sitios: any;

  constructor(public navCtrl: NavController, public navParams: NavParams,public db : DbProvider) {
  }

  ionViewDidLoad() {
    console.log('ionViewDidLoad Listado');
  }

  ionViewDidEnter(){
     this.db.getSitios().then((res)=>{
    this.sitios = [];
    for(var i = 0; i < res.rows.length; i++){
        this.sitios.push({
          id: res.rows.item(i).id,
          lat: res.rows.item(i).lat,
          lng: res.rows.item(i).lng,
          address: res.rows.item(i).address,
          description: res.rows.item(i).description,
          foto: res.rows.item(i).foto
        });
    }

    },(err)=>{ /* alert('error al sacar de la bd'+err) */ })
   }

}

Al llamar a this.db.getSitios obtenemos una promesa donde en res obtenemos el recurso devuelto por la base de datos. No podemos acceder directamente a los registros extraidos de la base de datos, debemos llamar a res.rows.item, y pasarle entre paréntesis el indice del elemento que queremos obtener, para ello recorremos res.rows mediante un bucle for y añadimos cada item al array this.sitios.

Al finalizar el bucle  this.sitios contendrá un array con todos los sitios que hemos guardados en la base de datos.

Ahora solo nos queda mostrarlos en el listado:

Vamos a editar la vista de la página listado, para ello abrimos el archivo listado.html, borramos el texto provisional que teníamos y lo dejamos de la siguiente manera:

<!--
  Generated template for the Listado page.

  See http://ionicframework.com/docs/v2/components/#navigation for more info on
  Ionic pages and navigation.
-->
<ion-header>

  <ion-navbar>
    <ion-title>Listado</ion-title>
  </ion-navbar>

</ion-header>

<ion-content padding>
<ion-list>
  <ion-item *ngFor="let sitio of sitios">
    <ion-thumbnail item-left>
      <img [src]="sitio.foto">
    </ion-thumbnail>
    <h2>{{ sitio.address }}</h2>
    <p>{{ sitio.description }}</p>
  </ion-item>
</ion-list>
</ion-content>

Como vemos dentro de ion-content hemos creado un elemento ion-list, después con *ngFor recorremos nuestro array de sitios creando un elemento ion-item por cada iteración.

Con ion-thumbnail mostramos una miniatura de la foto del sitio, despues mostramos la dirección y la descripción.

Como veis no tiene mayor dificultad. Si queréis saber más sobre el componente ion-list y sus posibilidades podçeis consultar la documentación oficial siguiendo este enlace:

https://ionicframework.com/docs/components/#lists

Si todo ha ido bien la pantalla listado debería tener un aspecto similar a este, bueno… tal vez en tus fotos no salga un gato ;-P

 

Recordad que como estamos utilizando plugins nativos necesitamos ejecutar la aplicación en un emulador o en un dispositivo físico escribiendo en consola:

ionic cordova run android

Esto es todo de momento, hoy hemos aprendido como guardar datos en una base de datos local con SQLite, hemos aprendido a crear un provider para utilizarlo como servicio para gestionar los accesos a la base de datos y hemos aprendido como funcionan los ciclos de vida de un página.

En el siguiente post seguiremos avanzando con nuestra app. Hay muchas cosas que se pueden mejorar, aquí solo pretendo explicar los conceptos básicos para que podáis crear vuestras propias aplicaciones. Os animo a que experimentéis y tratéis de mejorar la app, es sin duda la mejor forma de aprender. Podéis compartir vuestra experiencias en los comentarios y así ayudar a los que tengan algún problema.

 

Si necesitas desarrollar una aplicación móvil no dudes en solicitarme un presupuesto sin compromiso: