Archivo de la etiqueta: tutorial gratuito ionic 3 en español

Como mejorar el rendimiento de ionic en iOS y solución al problema de las peticiones http CORS

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:

Esta semana he estado trabajando en una aplicación con ionic para un cliente.

La aplicación funcionaba estupendamente en todos los dispositivos Android donde la he probado, evidentemente en dispositivos más antiguos el funcionamiento era un poco menos fluido pero aceptable en cualquier caso, sin embargo a la hora de probar la app en un dispositivo iOS, concretamente en un iPhone 6 que tengo para probar las apps en un dispositivo real me he llevado una decepción.

La aplicación tiene que mostrar una lista con imágenes e información bastante grande, además tiene un buscador para filtrar el listado por varios campos.

El scroll no iba muy fino a pesar de utilizar [virtualScroll] para el listado, cuando filtraba el listado por algunos campos a la hora de refrescar el contenido del listado iba a trompicones y tardaba en responder… en fin, la experiencia de usuario dejaba bastante que desear.

Lo primero que he hecho es pulir el código todo lo que he podido intentando que sea lo más eficiente posible, pero no ha sido suficiente.

Investigando un poco en el blog oficial de ionic me he encontrado con WKWebView.

Podéis leer la entrada del blog de ionic en este enlace:

http://blog.ionic.io/cordova-ios-performance-improvements-drop-in-speed-with-wkwebview/

Como sabemos ionic utiliza apache cordova que a su vez utiliza la webview del sistema para mostrar el contenido de nuestra aplicación.

Actualmente, la plataforma iOS proporciona dos webviews diferentes.

Está la webview más antigua (y más lenta) llamada “UIWebView” y otra mas nueva llamada “WKWebView“.

El navegador web predeterminado de iOS es Safari, internamente Safari utiliza WKWebView, sin embargo ionic debido a diversas incompatibilidades y problemas  técnicos utiliza UIWebView.

WKWebView es más rápida que UIWebView, además Apple proporciona actualizaciones en cada versión de iOS.

Para utilizar WKWebView solo tenemos que instalar en nuestra aplicación un plugin que proporciona ionic-team.

Para instalar el plugin desde consola ejecutamos el siguiente comando:

ionic cordova plugin add https://github.com/ionic-team/cordova-plugin-wkwebview-engine.git --save

Solo con instalar este plugin mi aplicación iba muchísimo mas fluida en mi iPhone.

Probad vuestra aplicación con este plugin y si todo va bien perfecto, sin embargo yo tuve problemas con CORS  (Cross Origin Resource Sharing) al hacer peticiones al servidor ya que yo no tenia acceso para cambiar nada en el servidor y por lo tanto no podía cambiar las cabeceras de respuesta del servidor para aceptar peticiones cross origin.

Esto lo podemos resolver de dos maneras, utilizando en plugin de ionic native HTTP: https://ionicframework.com/docs/native/http/

El único problema es que sólo funciona en el dispositivo y no proporciona toda la potencia del servicio Http de Angular.

Para solucionar esto podemos utilizar ionic-native-http-connection-backend que podéis encontrar en github: https://github.com/sneas/ionic-native-http-connection-backend

La forma de instalarlo sería:

npm install ionic-native-http-connection-backend --save
ionic cordova plugin add cordova-plugin-http2

Después en app.module.ts tendríamos que añadir lo siguiente:

import { NgModule } from '@angular/core';
import { NativeHttpFallback, NativeHttpModule } from 'ionic-native-http-connection-backend';
import { RequestOptions, Http } from '@angular/http';

@NgModule({
    declarations: [],
    imports: [
        NativeHttpModule
    ],
    bootstrap: [],
    entryComponents: [],
    providers: [
        {provide: Http, useClass: Http, deps: [NativeHttpFallback, RequestOptions]}
    ],
})
export class AppModule {
}

Una vez hecho esto ya podemos realizar peticiones http sin problemas, si quieres saber como relizar peticiones http con ionic puedes consultar el siguiente post: Tutorial de Ionic – Peticiones http – API REST

Con estas dos cosas he conseguido que la aplicación funcione correctamente y con una fluidez mas que aceptable en iOS.

Bueno, por hoy aquí lo dejo con estos pequeños consejos, espero que os sea útil.

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

Tutorial de Ionic – Firebase – parte 2: Database – Guardar nuestros sitios en la nube.

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:

El el post anterior vimos como autenticar un usuario en firebase con email y contraseña, hoy vamos ha ver como utilizar Firebase Database para guardar nuestros sitios en la nube.

Para ello vamos a crear un provider para gestionar nuestros sitios en firebase Database, por lo tanto desde consola nos situamos dentro de la carpeta de nuestro proyecto y creamos en nuevo provider:

ionic g provider firebaseDb

Ahora editamos el archivo firebase-db.ts que se acaba de generar dentro de la carpeta providers/firebase-db, eliminamos el import Http y rxjs/add/operator/map e importamos AngularFireDatabase, FirebaseListObservable  y nuestro provider AuthProvider quedando de la siguiente manera:

import { Injectable } from '@angular/core';
import { AngularFireDatabase, AngularFireList } from 'angularfire2/database';
import { AuthProvider } from '..auth/auth';
/*
  Generated class for the FirebaseDbProvider provider.

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

  constructor(public afDB: AngularFireDatabase, public auth: AuthProvider) {
    console.log('Hello FirebaseDbProvider Provider');
  }
}

Como necesitamos el id del usuario lo primero que vamos ha hacer es añadir la función getUser al provider auth que creamos en el post anterior, por lo tanto editamos auth.ts y añadimos esta función:

// Obtenemos el id de usuario.
 getUser(){
    return this.afAuth.auth.currentUser.uid;
 }

Bien, ahora que ya podemos obtener el id de usuario vamos a añadir en el archivo firebase-db.ts un método para guardar nuestros sitios en Firebase database:

guardaSitio(sitio){
     sitio.id  = Date.now();
     return this.afDB.database.ref('sitios/'+this.auth.getUser()+'/'+sitio.id).set(sitio)
  }

Como vemos la función recibe como parámetros  sitio que será un objeto con los datos de nuestro sitio.

Al objeto sitio le añadimos un campo id para identificarlo y así poder luego modificarlo. Como necesitamos que el id sea diferente cada vez vamos a utilizar Date.now() que nos devuelve los milisegundos transcurridos desde el 1 de enero de 1970, con esto  nos aseguramos que no se repita el id, a no ser que seas capaz de guardar dos sitios en menos de un milisegundo ;-P.

En firebase se guarda la información con estructura de árbol en formato JSON. Para acceder a ella tenemos que hacer referencia a la “ruta” a la que queremos acceder.

En este caso le estamos diciendo que guarde nuestro sitio con esta estructura ‘sitio/_id_usuari_/_id_sitio_/_sitio_’.

El id de usuario lo obtenemos con la función que acabamos de definir en  AuthProvide  this.auth.getUser().

Dentro de sitio colgarán los diferentes id de usuarios de los cuales a su vez colgarán los diferentes sitios de cada usuarios.

Para verlo mas claro en el modal nuevo-sitio vamos a modificar el método guardarSitio para que en lugar de guardar el sitio en la base de datos local SQlite lo guarde en firebase y podamos ver la estructura de como se guarda la información en firebase.

Editamos modal-nuevo-sitio.ts y hacemos los siguientes cambios:

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';
import { FirebaseDbProvider } from '../../providers/firebase-db/firebase-db';


/**
 * Generated class for the ModalNuevoSitioPage 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
    private dbFirebase :FirebaseDbProvider,
  ) {

  }

  ionViewDidLoad() {
    console.log('ionViewDidLoad ModalNuevoSitioPage');
      this.coords.lat = this.navParams.get('lat');
      this.coords.lng = this.navParams.get('lng');
       this.getAddress(this.coords).then(results=> {
        this.address = results[0]['formatted_address'];
      }, errStatus => {
          // Aquí iría el código para manejar el error
      });
  }

  cerrarModal(){
    this.viewCtrl.dismiss();
  }

  getAddress(coords):any {
    var geocoder = new google.maps.Geocoder();

    return new Promise(function(resolve, reject) {
        geocoder.geocode({'location': coords} , function (results, status) { // llamado asincronamente
            if (status == google.maps.GeocoderStatus.OK) {
                resolve(results);
            } else {
                reject(status);
            }
        });
    });
  }

  sacarFoto(){

    let cameraOptions : CameraOptions = {
        quality: 50,
        encodingType: this.camera.EncodingType.JPEG,
        targetWidth: 800,
        targetHeight: 600,
        destinationType: this.camera.DestinationType.DATA_URL,
        sourceType: this.camera.PictureSourceType.CAMERA,
        correctOrientation: true
    }


    this.camera.getPicture(cameraOptions).then((imageData) => {
      // imageData is a base64 encoded string
        this.foto = "data:image/jpeg;base64," + imageData;
    }, (err) => {
        console.log(err);
    });
  }

  guardarSitio(){
    let sitio = {
      lat: this.coords.lat,
      lng: this.coords.lng ,
      address: this.address,
      description: this.description,
      foto: this.foto
    }
  
this.dbFirebase.guardaSitio(sitio).then(res=>{
        console.log('Sitio guardado en firebase:');
        this.cerrarModal();
    })
   }
}

Como ahora vamos a guardar nuestros sitios en firebase he comentado el import de DbProvider ya que ahora no lo vamos a utilizar, y en la función guardarSitio hemos sustituido la linea this.db.addSitio… por this.dbFirebase.guardaSitio…

Como vemos llamamos a la función guardaSitio del provider FirebaseDbProvider que hemos creado más arriba y le pasamos como parámetro el objeto sitio con los datos de nuestro sitio.

Ahora ejecutamos nuestra aplicación y vamos a la consola de firebase, antes de guardar ningún sitio si seleccionamos Database en el menú de la izquierda de la consola de firebase veremos algo como esto:

Ahora vamos a guardar un nuevo sitio desde nuestra app, rellenamos los campos del formulario del modal nuevo sitio y le damos a guardar, si todo ha ido bien ahora en la consola de firebase veremos algo como esto (pulsa en el icono ‘+’ para desplegar los campos):

Como vemos los datos de guardan en una estructura de árbol, en el primer nivel esta sitio, de sitio ‘cuelga’ los id de usuario, en este caso solo tenemos el nuestro pero si distribuís la aplicación por cada usuario habrá un nodo, de cada usuario ‘cuelgan’ los ids de cada sitio, y de cada id a su vez cuelgan los campos del sitio.

Ahora que ya podemos guardar nuestros sitios en firebase vamos a ver como podemos obtener todos los sitios que tenemos guardados para mostrarlos en el listado:

Lo primero que vamos ha hacer es crear una función en firebase-db.ts para obtener el listado de sitios guardados en firebase database:

getSitios(){
    return this.afDB.list('sitios/'+this.auth.getUser()).valueChanges();
  }

Para obtener el listado de sitios guardados utilizamos el método list de AngularFireDatabase pasando como parámetro la ruta a partir de la cual queremos obtener los datos, en este caso queremos obtener todo lo que cuelgue de sitios/id_usuario,  es decir todos los sitios de nuestro usuario, el id de usuario una vez más lo obtenemos con this.auth.getUser() que hemos creado en nuestro provider AuthProvider.

Con .valueChanges() devolvemos un observable cuando se produzcan cambios en la base de datos.

Bien, llegados a este punto vamos a modificar el controlador de la página listado para que en lugar de obtener los datos de la base de datos local los obtenga directamente de firebase.

Editamos listado.ts e importamos FirebaseDbProvider:

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


/**
 * 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 db : DbProvider,
    public modalCtrl : ModalController,
    public alertCtrl : AlertController,
    public dbFirebase :FirebaseDbProvider,

  ) {

  }

Ahora en la función ionViewDidEnter() vamos a sustituir this.db.getSitios().then((res)=>{…}) por lo siguiente:

ionViewDidEnter(){
   
    this.dbFirebase.getSitios().subscribe(sitios=>{
      this.sitios = sitios;
    })

}

La función getSitios() que como hemos visto a su vez llama al método list de AngularFireDatabase nos devuelve un observable, por lo que nos suscribimos al resultado. Aquí podemos ver claramente la diferencia entre un observable y una promesa.

Guardamos en this.sitios la lista de sitios que obtenemos lo que hará que se refresque automáticamente en la vista del listado.

Como ya sabemos una promesa ejecuta lo que tengamos definido en  then(res=>{ …}) una vez tenga listo el resultado, pero esto se ejecuta una única vez, sin embargo un observable va a ejecutar lo que tengamos definido en .subscribe(res=>{…}) cada vez que haya un cambio en el resultado. Por ejemplo si desde la consola de firebase cambiamos a mano el campo description de nuestro sitio, este se verá automáticamente reflejado en nuestra aplicación, es interesante hacer la prueba.

En este momento ya podemos guardar sitios en firebase y mostrarlos en el listado:

Lo siguiente que vamos ha hacer es la modificación de los sitios guardados.

Para ello primero vamos ha hacer una pequeña modificación a la función guardaSitio() de nuestro provider firebase-db.ts:

guardaSitio(sitio){
   if(!sitio.id){
      sitio.id  = Date.now();
    }
    return this.afDB.database.ref('sitios/'+this.auth.getUser()+'/'+sitio.id).set(sitio)
  }

Hemos añadido un if para comprobar si el sitio que recibimos en la función tiene el campo id definido.

Como utilizamos el id del sitio para establecer la ruta del registro, si el sitio que recibimos para guardar no tiene id significa que es un sitio nuevo y entonces le damos un id, si ya tiene un id significa que es un sitio que ya existe y hay que modificar.

Modificar un registro en firebase se hace exactamente igual que crear uno nuevo, si la ruta a la que hacemos referencia no existe crea el registro, si ya existe entonces modifica el registro existente en firebase.

Ahora vamos a  modificar el archivo modal-detalle-sitio.ts para hacer que al guardar los cambios al editar un sitio existente se guarden los cambios en firebase, para ello vamos a importar FirebaseDbProvider para poder llamar a la función guardaSitio que acabamos de modificar, y vamos también a modificar la función guardarCambios para que guarde los cambios en firebase en lugar de en la base de datos local:

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

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

@IonicPage()
@Component({
  selector: 'page-modal-detalle-sitio',
  templateUrl: 'modal-detalle-sitio.html',
})
export class ModalDetalleSitioPage {

  sitio: any;
  edit : boolean = false;

  constructor(
    public navCtrl: NavController,
    public navParams: NavParams,
    private viewCtrl : ViewController,
    private launchNavigator : LaunchNavigator,
    private camera: Camera,
  //  private db: DbProvider,
    private dbFirebase :FirebaseDbProvider

  ) {
     this.sitio = this.navParams.data;
  }

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

  cerrarModal(){
    this.viewCtrl.dismiss();
  }

  comoLlegar(){
    let destino = this.sitio.lat+', '+this.sitio.lng;
    this.launchNavigator.navigate(destino)
    .then(
      success => console.log('Launched navigator'),
      error => console.log('Error launching navigator', error)
    );
 }

 editar(){
   this.edit = true;
 }

 sacarFoto(){

    let cameraOptions : CameraOptions = {
        quality: 50,
        encodingType: this.camera.EncodingType.JPEG,
        targetWidth: 800,
        targetHeight: 600,
        destinationType: this.camera.DestinationType.DATA_URL,
        sourceType: this.camera.PictureSourceType.CAMERA,
        correctOrientation: true
    }


    this.camera.getPicture(cameraOptions).then((imageData) => {
      // imageData is a base64 encoded string
        this.sitio.foto = "data:image/jpeg;base64," + imageData;
    }, (err) => {
        console.log(err);
    });
  }

  guardarCambios(){

     let sitio = {
      id : this.sitio.id,
      lat: this.sitio.lat,
      lng: this.sitio.lng ,
      address: this.sitio.address,
      description: this.sitio.description,
      foto: this.sitio.foto
    }

    this.dbFirebase.guardaSitio(sitio).then(res=>{
        console.log('Sitio modificado en firebase');
        this.cerrarModal();
    })
   }

}

Hemos comentado el import de DbProvider porque ya no lo estamos utilizando.

Ahora si pruebas la aplicación con el panel de firebase abierto podrás ver que si modificas un sitio que tengas guardado automáticamente se verá reflejado este cambio en el registro de firebase.

Para concluir solo nos queda eliminar sitios.

Vamos a añadir a  FirebaseDbProvider en el archivo firebase-db.ts una función para eliminar un sitio de la base de datos de firebase:

public borrarSitio(id){
        this.afDB.database.ref('sitios/'+this.auth.getUser()+'/'+id).remove();

}

Como puedes ver es muy sencillo, solo necesitamos recibir el id del sitio que queremos eliminar y haciendo referencia a la ruta de nuestro sitio (que una vez más es sitios/_id_usuario_/id_sitio) utilizamos la función remove() para eliminar el sitio.

Ahora en el listado solo tenemos que sustituir la llamada a borrarSitio() de la base de datos local  de DbProvider por la función que acabamos de crear en FirebaseDbProvider, por lo tanto editamos el archivo listado.ts y dejamos la función borrarSitio() 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 firebase
              this.dbFirebase.borrarSitio(id);
           }
        }
      ]
    });

    alert.present();

 }

Como podemos observar ya no necesitamos obtener de nuevo los sitios una vez borrado para que se refresque el listado ya que al ser firebase una base de datos en tiempo real el listado se actualiza automáticamente.

Podéis probar a borrar un sitio y veréis como el sitio se elimina automáticamente en el panel de firebase y el listado se actualiza.

Eso es todo por hoy, con esto ya podemos hacer muchas cosas interesantes utilizando firebase, seguro que se os ocurren grandes ideas para realizar apps utilizando firebase como backend. Podéis dejarme en los comentarios  esas grandes ideas, no se lo contaré a nadie ;-P

 

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

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 7 – Modificar nuestros sitios.

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 último post vimos como mostrar en un modal los sitios guardados y como abrir el navegador GPS para que nos marque la ruta hasta el.

Hoy vamos seguir desarrollando la app para poder modificar los sitios que tenemos guardados.

Lo primero que vamos a hacer es crear una variable miembro en el controlador del modal modal-detalle-sitio.ts a la que vamos a llamar edit y que será del tipo boolean por lo que la añadimos debajo de la variable sitio que ya teníamos definida:

...

export class ModalDetalleSitio {

  sitio: any;
  edit : boolean = false;
  constructor(public navCtrl: NavController, public navParams: NavParams, private viewCtrl : ViewController) {

...

Esta variable nos servirá para mostrar u ocultar el formulario de edición del sitio en función de si vale true o false. De inicio la ponemos a false para que no se muestre el formulario hasta que pulsemos en el botón editar.

Bien, ahora vamos a modificar la vista para añadir el formulario y el botón de editar.

El formulario será muy parecido al que pusimos en la vista del modal nuevo-sitio.

Editamos el archivo modal-detalle-sitio.html y añadimos el siguiente código marcado con fondo amarillo:

<!--
  Generated template for the ModalDetalleSitio page.

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

  <ion-navbar>
    <ion-title>{{ sitio.address }}</ion-title>
     <ion-buttons start>
      <button ion-button (click)="cerrarModal()">
        <ion-icon name="md-close"></ion-icon>
      </button>
    </ion-buttons>
  </ion-navbar>

</ion-header>


<ion-content padding>
  <ion-card *ngIf="!edit">
    <img [src]="sitio.foto" *ngIf="sitio.foto" />
    <ion-card-content>
       <ion-item text-wrap>
          <h2>{{ sitio.address }}</h2>
           <p>{{ sitio.description }}</p>
        </ion-item>
    </ion-card-content>
    <ion-row>
      <ion-col text-center>
         <button ion-button icon-left clear small (click)="comoLlegar()">
           <ion-icon name="navigate"></ion-icon>
           <div>Como llegar</div>
         </button>
      </ion-col>
      <ion-col text-center>
         <button ion-button icon-left clear small (click)="editar()">
           <ion-icon name="editar"></ion-icon>
           <div>Editar</div>
         </button>
      </ion-col>
    </ion-row>
</ion-card>
 <ion-card *ngIf="edit">
      <ion-card-content>
      <form (ngSubmit)="guardarCambios()">
      <ion-item>
        <img [src]="sitio.foto" *ngIf="sitio.foto" />
        <button ion-button icon-left full type="button" (tap)="sacarFoto()">
          Foto&nbsp;&nbsp;
          <ion-icon name="camera"></ion-icon>
        </button>
      </ion-item>
      <hr/>
      <ion-item>
        <ion-label>Descripción</ion-label>
        <ion-textarea [(ngModel)]="sitio.description" name="description"></ion-textarea>
      </ion-item>
      <button ion-button type="submit" block>Actualizar Sitio</button>
    </form>
    </ion-card-content>
    </ion-card>
</ion-content>

Vayamos por partes:

En primer lugar al ion-card que ya teníamos y que muestra los datos del sitio le hemos añadido *ngIf=”!edit”, con esto le indicamos que solo se muestre si la variable edit está a false, es decir cuando no se está editando:

  <ion-card *ngIf="!edit">

Después debajo del botón “Como llegar” hemos creado otra columna (ion-col) donde mostramos el botón Editar, en el evento click del botón editar llamamos a la función editar() que posteriormente definiremos en el controlador, aunque ya adelanto que simplemente cambiará el valor de la variable edit a true:

 <ion-col text-center>
         <button ion-button icon-left clear small (click)="editar()">
           <ion-icon name="editar"></ion-icon>
           <div>Editar</div>
         </button>
      </ion-col>

Después hemos añadido otro elemento ion-card al que le hemos puesto la directiva condicional *ngIf=”edit”, al contrario que el card anterior se mostrará cuando la variable edit valga true, es decir, que dependiendo del valor de edit se mostrará una cosa o la otra.

 <ion-card *ngIf="edit">

Después dentro de ion-card-content hemos creado un formulario para editar los campos modificables del sitio, en este caso la descripción y la foto.

En el evento (ngSubmit) del formulario llamamos a la función guardarCambios() que definiremos después en el controlador:

      <form (ngSubmit)="guardarCambios()">

Al igual que teníamos en el formulario del modal nuevo sitio, tenemos un item con una imagen que mostramos solo si la variable sitio.foto contiene algún valor (*ngIf=”sitio.foto):

<img [src]="sitio.foto" *ngIf="sitio.foto" />

Después tenemos el botón para sacar una nueva foto:

 <button ion-button icon-left full type="button" (tap)="sacarFoto()">
          Foto&nbsp;&nbsp;
          <ion-icon name="camera"></ion-icon>
        </button>

Al pulsar en el botón se llamará a la función sacarFoto que deberemos definir en el controlador.

Después tenemos otro item con el text-area para modificar la descripción del sitio:

<ion-item>
        <ion-label>Descripción</ion-label>
        <ion-textarea [(ngModel)]="sitio.description" name="description"></ion-textarea>
      </ion-item>

Por último antes de cerrar el form tenemos un botón para guardar los cambios:

<button ion-button type="submit" block>Actualizar Sitio</button>

Recordad que al pulsar el botón se lanzará el formulario que en el evento submit llama a la función guardarCambios().

Bien, ahora que ya tenemos preparada la vista tenemos que definir en el controlador todos los métodos que vamos a necesitar por lo que vamos a editar el archivo modal-detalle-sitio.ts.

Lo primero de todo, como vamos a utilizar la cámara y también vamos a necesitar el provider db.ts que tenemos creado para gestionar la base de datos, necesitamos importarlos e inyectarlos en el constructor por lo tanto editamos el archivo modal-detalle-sitio.ts y añadimos el siguiente código:

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



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

  sitio: any;
  edit : boolean = false;
  constructor(public navCtrl: NavController, public navParams: NavParams, private viewCtrl : ViewController, private launchNavigator : LaunchNavigator, private camera: Camera, private db: DbProvider ) {
      this.sitio = this.navParams.data;
}

...

Bien, ahora vamos a definir la función editar que se ejecuta al pulsar el botón editar, como podemos observar simplemente cambia la variable this.edit a true:

editar(){
   this.edit = true;
 }

Si ejecutáis ahora la aplicación en el móvil veréis que al pulsar sobre el botón editar desaparece el card con los datos y aparece el pequeño formulario para cambiar la foto y la descripción. Evidentemente no podemos tomar una nueva foto ni guardar los cambios ya que todavía no hemos definido estas funciones en el controlador, por lo tanto vamos a definir ahora la función sacarFoto():

sacarFoto(){

    let cameraOptions : CameraOptions = {
        quality: 50,
        encodingType: this.camera.EncodingType.JPEG,
        targetWidth: 800,
        targetHeight: 600,
        destinationType: this.camera.DestinationType.DATA_URL,
        sourceType: this.camera.PictureSourceType.CAMERA,
        correctOrientation: true
    }


    this.camera.getPicture(cameraOptions).then((imageData) => {
      // imageData is a base64 encoded string
        this.sitio.foto = "data:image/jpeg;base64," + imageData;
    }, (err) => {
        console.log(err);
    });
  }

Como podéis observar esta función es exactamente igual que la que definimos en el modal nuevo sitio, la única diferencia es que la foto resultante se la asignamos a la variable this.sitio.foto en lugar de a this.foto.

Si tenéis alguna duda sobre como obtener una fotografía de la cámara del móvil podéis repasar la parte 4 de este tutorial donde vimos con más detalle como utilizar la cámara del móvil para obtener una foto.

Por último necesitamos guardar los cambios en la base de datos así que vamos a definir el método guardarCambios() que se ejecuta al lanzar el formulario con el botón “Actualizar Sitio”:

guardarCambios(){
    this.db.modificaSitio(this.sitio).then((res)=>{
        this.edit = false;
    },(err)=>{  /* alert('error al meter en la bd'+err) */ })
  }

Llamamos a una función del provider db llamada modificaSitio y le pasamos como parámetro  this.sitio, si todo ha ido bien nos devolverá una promesa, al ejecutar la promesa ponemos la variable this.edit a false para que se oculte el formulario y se vuelva a mostrar la información del sitio.

Nos quedaría crear la función modificaSitio en el provider para guardar las modificaciones en la base de datos, por lo tanto vamos a editar el archivo db.ts que se encuentra en la carpeta providers y vamos a añadir la siguiente función:

public modificaSitio(sitio){
    let sql = "UPDATE sitios  SET lat = ?, lng = ?, address = ?, description = ?, foto = ? WHERE id = ? ";
    return this.db.executeSql(sql,[sitio.lat,sitio.lng,sitio.address,sitio.description,sitio.foto, sitio.id]);
  }

En función modificaSitio ejecutamos la sql para modificar los datos del sitio en la base de datos.

Las ‘?‘ se sustituyen por los valores en el orden en que se encuentran en el array que se le pasa como segundo parámetro al método this.db.executeSql.

Por si alguno se ha despistado os pongo a continuación el código completo hasta el momento del provider db.ts: 

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

/*
  Generated class for the DbProvider 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 DbProvider 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,{});
  }

  public modificaSitio(sitio){
    let sql = "UPDATE sitios  SET lat = ?, lng = ?, address = ?, description = ?, foto = ? WHERE id = ? ";
    return this.db.executeSql(sql,[sitio.lat,sitio.lng,sitio.address,sitio.description,sitio.foto, sitio.id]);
  }

}

También os dejo a continuación como tiene que quedar el archivo modal-detalle-sitio.ts:

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';
import { LaunchNavigator } from '@ionic-native/launch-navigator';

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

  sitio: any;
  edit : boolean = false;


  constructor(public navCtrl: NavController, public navParams: NavParams, private viewCtrl : ViewController, private camera: Camera, private db: DbProvider, private launchNavigator : LaunchNavigator) {
     this.sitio = this.navParams.data;
  }

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

  cerrarModal(){
    this.viewCtrl.dismiss();
  }

  comoLlegar(){

    let destino = this.sitio.lat+', '+this.sitio.lng;

    this.launchNavigator.navigate(destino)
    .then(
      success => console.log('Launched navigator'),
      error => console.log('Error launching navigator', error)
    );

 }

 editar(){
   this.edit = true;
 }

 sacarFoto(){

    let cameraOptions : CameraOptions = {
        quality: 50,
        encodingType: this.camera.EncodingType.JPEG,
        targetWidth: 800,
        targetHeight: 600,
        destinationType: this.camera.DestinationType.DATA_URL,
        sourceType: this.camera.PictureSourceType.CAMERA,
        correctOrientation: true
    }


    this.camera.getPicture(cameraOptions).then((imageData) => {
      // imageData is a base64 encoded string
        this.sitio.foto = "data:image/jpeg;base64," + imageData;
    }, (err) => {
        console.log(err);
    });
  }

  guardarCambios(){
    this.db.modificaSitio(this.sitio).then((res)=>{
    //  alert(res);
      for (var i in res)
      {
        alert(i+' - '+res[i]);
      }
        this.edit = false;
    },(err)=>{   alert('error al meter en la bd'+err)  })
  }


}

Eso es todo por hoy, en el siguiente post seguiremos desarrollando la app.

 

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