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聽yvamos 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: