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: