Archivo de la etiqueta: geolocalizaci贸n

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 6 – Mostrar detalles del sitio guardado y abrir navegador gps para llegar a el.

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 guardar nuestros sitios en la base de datos y como mostrar los sitios que tenemos guardados en la p谩gina listado.

Hoy vamos a mostrar el detalle del sitio聽al pulsar sobre el en el listado. La idea es que nos muestre la direcci贸n, la foto y la descripci贸n que hayamos introducido, adem谩s vamos a a帽adir un bot贸n de “Como llegar” para que nos abra el navegador gps del m贸vil con la direcci贸n hac铆a dicho sitio. Ya no volveremos a olvidar donde hemos aparcado el coche, o podremos recordar como volver a ese restaurante que tanto nos gust贸.

Lo primero que tenemos que hacer es crear un nuevo modal con ionic generator:

ionic g page modalDetalleSitio

Ahora en listado.ts debemos importar el componente聽ModalController聽 de la librer铆a ionic-angular, por lo tanto despu茅s de NavController y NavParams importamos tambi茅n ModalController.

import { IonicPage, NavController, NavParams, ModalController } from 'ionic-angular';

Debemos inyectar tambi茅n en el constructor el componente ModalController al que hemos llamado modalCtrl:

constructor(
    public navCtrl: NavController,
    public navParams: NavParams,
    public modalCtrl : ModalController,
    public db: DbProvider) { ......

Ahora ya podemos crear un m茅todo en listado.ts聽para abrir el modal que acabamos de crear pasandole como par谩metro el sitio que queremos mostrar:

muestraSitio(sitio){
      let modalSitio = this.modalCtrl.create( 'ModalDetalleSitioPage', sitio );
      modalSitio.present();
   }

Vamos a modificar el listado para que al pulsar sobre un sitio de la lista se abra el modal por lo tanto editamos listado.html y a帽adimos lo siguiente:

<!--
聽聽Generated template for the Listado 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 *ngFor="let sitio of sitios" (click)="muestraSitio(sitio)">
    <ion-thumbnail item-left>
      <img [src]="sitio.foto">
    </ion-thumbnail>
    <h2>{{ sitio.address }}</h2>
    <p>{{ sitio.description }}</p>
  </ion-item>
</ion-list>
</ion-content>

Con esto llamamos al m茅todo muestraSitio que acabamos de definir en el controlador listado.ts聽y le pasamos como par谩metro el sitio sobre el se ha hecho click.

Ahora vamos a editar el archivo modal-detalle-sitio.ts聽y vamos a importar e inyectar en en constructor el elemento聽ViewController聽que lo utilizaremos para poder cerrar el modal:

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams, ViewController } from 'ionic-angular';

/**
聽* 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 {

  constructor(public navCtrl: NavController, public navParams: NavParams, private viewCtrl : ViewController) {
}

...

Vamos a crear tambi茅n una funci贸n para cerrar el modal a la que luego llamaremos desde el bot贸n de cerrar de la vista:

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

Ahora vamos a crear una variable miembro llamada sitio donde vamos a guardar el objeto con los datos del sitio que recibimos desde NavParams como par谩metro al abrir el modal desde la p谩gina listado:

...
export class ModalDetalleSitioPage {

聽聽sitio: any;
  constructor(public navCtrl: NavController, public navParams: NavParams, private viewCtrl : ViewController) {
      this.sitio = this.navParams.data;
}

...

Ahora ya estamos listos para poder mostrar los datos del sitio el la vista, as铆 que editamos el archivo聽modal-detalle-sitio.html聽e introducimos el siguiente c贸digo:

<!--
聽聽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>
    <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-row>
</ion-card>
</ion-content>

Como t铆tulo del modal hemos puesto la direcci贸n que est谩 almacenada en sitio.address.

Despu茅s a帽adimos el bot贸n para cerrar el modal, para ello le indicamos que en el evento click llame a la funci贸n cerrarModal que hemos definido en el controlador.

Despu茅s dentro de ion-content 聽creamos un elemento ion-card y dentromostramos la foto solo si existe con el condicional *ngIf.

Despu茅s dentro de ion-card-content 聽mostramos la direcci贸n y la descripci贸n.

Por 煤ltimo creamos un bot贸n con un icono de tipo navigate dentro de un elemento ion-col que a su vez esta dentro de un elemento ion-row. Hacemos esto para conseguir que el bot贸n se muestre centrado gracias a la propiedad text-center que hemos a帽adido a la etiqueta ion-col.

Para saber m谩s sobre los componentes disponibles en ionic pod茅is una vez m谩s leer la documentaci贸n oficial de ionic:聽https://ionicframework.com/docs/components/

Como podemos observar en el evento click del bot贸n llamamos a la funci贸n comoLlegar().

La idea es que cuando pulsemos en el bot贸n nos habr谩 el navegador gps que tengamos instalado por defecto en el m贸vil para indicarnos la ruta a seguir para llegar al lugar.

Vamos 聽ello vamos a crear la funci贸n comoLlegar en el controlador.

Aunque podr铆amos utilizar la funci贸n聽que ya expliqu茅 en est茅 post perteneciente a la serie de posts que publiqu茅 hace tiempo sobre google maps, vamos a utilizar en su lugar el plugin LaunchNavigator que ya nos ofrece esta funcionalidad de forma nativa (gracias a聽writ3r聽por el aporte).

Lo primero que debemos hacer es instalar el plugin escribiendo los siguientes comandos desde consola:

ionic cordova plugin add uk.co.workingedge.phonegap.plugin.launchnavigator
npm install --save @ionic-native/launch-navigator

Ahora debemos importar y declarar el plugin en 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 { Geolocation } from '@ionic-native/geolocation';
import { Camera } from '@ionic-native/camera';
import { DbProvider } from '../providers/db/db';
import { SQLite } from '@ionic-native/sqlite';
import { LaunchNavigator } from '@ionic-native/launch-navigator';

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

  ]
})
export class AppModule {}

Ahora editamos 聽el archivo modal-detalle-sitio.ts, importamos el plugin LauchNavigator, lo inyectamos en el constructor 聽y a帽adimos el m茅todo comoLlegar:

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams, ViewController } from 'ionic-angular';
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;

  constructor(public navCtrl: NavController, public navParams: NavParams, private viewCtrl : ViewController, 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)
    );

 }
}

El m茅todo navigate de launchNavigator recibe como par谩metro la direcci贸n donde queremos llegar, en este caso las coordenadas separadas por una coma que tenemos en las variables this.sitio.lat y this.sitio.lng.

Si ejecutamos nuestra aplicaci贸n en en m贸vil obtendremos algo como esto al entrar en el detalle de un sitio de la lista:

Modal detalle del sitio
Modal detalle del sitio

Bien , por hoy lo dejamos aqu铆, ya podemos guardar nuestros sitios favoritos, o recordar donde hemos aparcado el coche por ejemplo, podemos mostrar los sitios que hemos guardado y podemos abrir el navegador gps para poder llegar asta el lugar.

Aunque se puede mejorar mucho esta aplicaci贸n, ya podemos decir que es funcional y nos ha servido para aprender muchos conceptos sobre ionic. Como siempre os animo a que experiment茅is y trat茅is de mejorar la app por vuestra cuenta ya que es una magnifica forma de aprender y mejorar.

En el pr贸ximo post聽seguiremos desarrollando la app y veremos como modificar un sitio guardado.

 

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

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

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

 

隆隆Atenci贸n!! este tutorial se basa en ionic 3 y est谩 desactualizado por lo que es posible que los ejemplos no funcionen en la 煤ltima versi贸n de ionic, haz click aqu铆 para acceder a un tutorial mas actual de Ionic.

Hola a todos:

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

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

Provider

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

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

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

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

ionic g provider db

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

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

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

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

/*
聽聽Generated class for the Db provider.

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

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

}

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

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

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

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

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

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

/*
聽聽Generated class for the Db provider.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

    });
  }
}

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

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



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

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

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

...

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

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

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

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

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

Si todo ha ido bien聽cerramos el modal.

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

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

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

/*
聽聽Generated class for the Db provider.

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

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

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

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

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

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

}

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

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


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

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

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

...

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

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

Ciclo de vida de una p谩gina

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

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

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

Vamos a ver los m茅todos con los que cuenta ionic聽en funci贸n de los eventos del ciclo de vida de una p谩gina:

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

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

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

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

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

A帽adimos el m茅todo ionViewDidEnter al controlador de la p谩gina listado en el archivo listado.ts聽y hacemos聽la llamada para extraer nuestros sitios de la base de datos:

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


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

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

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

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

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

}

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

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

Ahora solo nos queda mostrarlos en el listado:

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

<!--
聽聽Generated template for the Listado page.

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

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

</ion-header>

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

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

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

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

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

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

 

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

ionic cordova run android

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

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

 

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

Tutorial de Ionic – Crear una aplicaci贸n para guardar nuestros sitios geolocalizados – Parte 4 – Mostrando la direcci贸n a partir de las coordenadas y sacando foto con la c谩mara.

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 poner un marcador al mapa, aprendimos a utilizar los botones FAB y aprendimos a utilizar ventanas modales.

Hoy vamos a continuar desarrollando nuestra app, vamos a crear un peque帽o formulario en el modal donde mediante una llamada a la api de google maps obtendremos y mostraremos la direcci贸n a partir de las coordenadas y permitiremos introducir una descripci贸n y 聽tambi茅n tomar una fotograf铆a del lugar.

Bien, vayamos por partes:

Ahora vamos聽a a帽adir al controlador del modal tres variables nuevas que vamos a necesitar:

  • Una聽variable de tipo string聽 que vamos a llamar address聽donde聽guardaremos la direcci贸n que luego mostraremos en la vista.
  • Una variable de tipo string a la que vamos a llamar description y que contendr谩 la descripci贸n del lugar que introduzcamos desde el formulario.
  • Por 煤ltimo una variable de tipo any que vamos a llamar foto, donde guardaremos una foto del lugar codificada en base 64.

Por lo tanto editamos el controlador de nuestro modal (modal-nuevo-sitio.ts) encima del constructor de la clase despu茅s de la variable coords聽definimos las siguientes variables:

...

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) {}

...

Para obtener la direcci贸n correspondiente a unas coordenadas Google maps cuenta con el elemento Geodecoder.聽

Vamos a ver brevemente como funciona Google Maps 聽Geodecoder:

Para realizar una petici贸n debemos crear un objeto dela clase Geodecoder y llamar al m茅todo geodecode pasandole las coordenadas y una funci贸n callback donde recibimos los datos en caso de tener 茅xito y el estado de la petici贸n:

var geocoder = new google.maps.Geocoder();

geocoder.geocode({'location': coords} , function (results, status) {
    if (status == google.maps.GeocoderStatus.OK) {
       // en results tenemos lo que nos devuelve la llamada;
    } else {
      // Ha habido alg煤n error
    }
});

Para obtener los resultados tenemos pasarle una funci贸n callback, sin embargo lo que nos interesa en que nos devuelva una promesa que es la mejor manera de gestionar las peticiones as铆ncronas desde el controlador.

Creando nuestras propias promesas

Ya vimos en el capitulo anterior como se tratan las funciones que nos devuelve una promesa utilizando then, pero si queremos que una funci贸n que creemos nosotros devuelva una promesa tenemos que devolver un objeto Promise donde la promesa se crea a partir de una funci贸n聽callback en el que ejecutaremos la sentencia que nos devolver谩 el resultado as铆ncrono y 聽llamaremos a las funciones pasadas como argumento resolve y reject. Si la operaci贸n se a ejecutado correctamente se llama a resolve, y si a ocurrido un error llamamos a reject.

Sabiendo esto para conseguir que geodecode nos devuelva la direcci贸n en una promesa tenemos que utilizar un peque帽o truco que consiste en crear una funci贸n聽que vamos a llamar getAddress聽y que haremos que nos devuelva una promesa con el resultado de la llamada a geodecode, para ello editamos el archivo modal-nuevo-sitio.ts a帽adimos la siguiente funci贸n en el controlador:

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);
            }
        });
    });
}

Como podemos ver la funci贸n retorna una promesa donde se le pasa como par谩metro una funci贸n con resolve y reject, luego dentro de la funci贸n se ejecuta la llamada a geodecoder.geodecode pas谩ndole las coordenadas y la funci贸n callback donde si se ha recibido como status聽google.maps.GeocoderStatus.OK significa que hemos recibido correctamente los datos y entonces ejecutamos resolve, de lo contrario ejecutamos reject.

De esta manera podemos hacer que geodecode nos devuelva una promesa.

Nota: al igual que hicimos en la pagina de inicio, para que聽Typescript no de error por no reconocer la clase google cuando la llamemos desde la funci贸n que vamos a crear, vamos a declarar la variable google justo debajo de los imports con declare聽var聽google:聽any;聽

Ahora en el m茅todo ionViewDidLoad聽que se ejecuta cuando la p谩gina se ha cargado vamos ha hacer una llamada a getAddress聽pas谩ndole las coordenadas que hemos recibido para obtener la direcci贸n y asign谩rsela a this.address:

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
      });
  }

Un ejemplo de la estructura de datos que recibimos en聽results聽ser铆a:

{
聽"results": [ {
聽"types": street_address,
聽"formatted_address": "Etorbidea Abandoibarra, 2, 48001 Bilbo, Bizkaia, Espa帽a",
聽"address_components": [ {
聽"long_name": "2",
聽"short_name": "2",
聽"types": street_number
聽}, {
聽"long_name": "Etorbidea Abandoibarra",
聽"short_name": "Etorbidea Abandoibarra",
聽"types": route
聽}, {
聽"long_name": "Bilbo",
聽"short_name": "Bilbo",
聽"types": [ "locality", "political" ]
聽}, {
聽"long_name": "Bizkaia",
聽"short_name": "BI",
聽"types": [ "administrative_area_level_2", "political" ]
聽}, {
聽"long_name": "Euskadi",
聽"short_name": "PV",
聽"types": [ "administrative_area_level_1", "political" ]
聽}, {
聽"long_name": "Espa帽a",
聽"short_name": "ES",
聽"types": [ "country", "political" ]
聽}, {
聽"long_name": "48001",
聽"short_name": "48001",
聽"types": postal_code
聽} ],
 "geometry": {
聽"location": {
聽"lat": 43.26861,
聽"lng": -2.934380000000033
聽},
 "location_type": "ROOFTOP",
 "viewport": {
聽"southwest": {
聽"lat": 43.26726101970851, 
聽"lng": -2.9357289802915147
聽},
 "northeast": {
聽"lat": 43.26995898029151,
聽"lng": -2.933031019708551
聽}
 }
 }
 } ]
}

Lo que nos interesa obtener que es la direcci贸n completa se encuentra en results[0][‘formatted_address’], por lo tanto 聽le asignamos este dato a this.address.

Mostrando las coordenadas y la 聽direcci贸n.

Ahora vamos a a帽adir en la vista un componente ion-card聽 donde mostraremos las coordenadas y la direcci贸n donde nos encontramos, editamos el archivo modal-nuevo-sitio.html聽para que quede de la siguiente manera:

<!--
聽聽Generated template for the ModalNuevoSitio page.

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

  <ion-navbar>
    <ion-title>Nuevo sitio</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>
    <ion-card-header>
      Localizaci贸n actual
    </ion-card-header>
    <ion-card-content>
      <p><strong>lat:</strong>{{ coords.lat}}<br/>
      <strong>lng:</strong>{{coords.lng }}</p>
      <hr>
      <p>{{ address }}</p>
    </ion-card-content>
  </ion-card>
</ion-content>

Como pod茅is observar hemos creado un componente ion-card que consta a su vez de un elemento ion-card-header donde ponemos como t铆tulo “Localizaci贸n 聽actual”, y en ion-car-content pondremos el contenido que queremos mostrar, en este caso mostramos un elemento <p> donde mostramos el valor de las variables coords.lat y coords.lng que hemos definido en el controlador y que contendr谩n las coordenadas actuales.

Por otro lado mostramos otro elemento <p> con el contenido de la variable address que de momento no contiene nada pero que contendr谩 la direcci贸n que corresponda con las coordenadas que hemos recogido.

Al pulsar en el FAB se abrir谩 el modal mostrando algo similar a esto:

Modal con las coordenadas y a direcci贸n
Modal con las coordenadas y a direcci贸n

Creando el formulario.

Vamos a seguir a帽adiendo elementos a la vista del modal. Adem谩s de las coordenadas y la direcci贸n queremos dar la posibilidad de tomar una foto del lugar y escribir anotaciones.
Para ello vamos a crear dentro del card, debajo de la direcci贸n un peque帽o formulario donde habr谩 un bot贸n para sacar una foto y un campo text-area para 聽escribir una descripci贸n del lugar.

Editamos de nuevo modal-nuevo-sitio.html聽y a帽adimos lo que est谩 marcado de amarillo:

<!--
聽聽Generated template for the ModalNuevoSitio page.

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

  <ion-navbar>
    <ion-title>Nuevo sitio</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>
    <ion-card-header>
     <strong>Localizaci贸n actual</strong>
    </ion-card-header>
    <ion-card-content>
      <p><strong>lat:</strong>{{ coords.lat}}<br/>
      <strong>lng:</strong>{{coords.lng }}</p>
      <hr>
      <p>{{ address }}</p>
      <hr/>
      <form (ngSubmit)="guardarSitio()">
      <ion-item>
        <img [src]="foto" *ngIf="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)]="description" name="description"></ion-textarea>
      </ion-item>
      <button ion-button type="submit" block>Guardar Sitio</button>
    </form>
    </ion-card-content>
    </ion-card>
</ion-content>

Como vemos al formulario le hemos a帽adido (ngSubmit), con esto le indicamos que cuando se ejecute el evento submit del formulario se ejecute la funci贸n guardarSitio definida en el controlador.

Despu茅s hemos a帽adido un elemento ion-item, donde vamos a mostrar la imagen con la fotograf铆a que tomemos:

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

Por un lado con [src] le indicamos que la imagen va a mostrar lo que contenga la variable foto, que hemos definido el el controlador y que al principio estar谩 vac铆a, y por otro lado con *ngIf le indicamos que solo se muestre la imagen si dicha variable foto tiene un valor, es decir聽que solo se mostrar谩 una vez hayamos tomado la fotograf铆a.

Despu茅s tenemos un bot贸n que al pulsar llama a la funci贸n sacarFoto que despu茅s definiremos en el controlador:

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

Con icon-left聽le indicamos al bot贸n que el icono se situar谩 a la izquierda, para mostrar el icono despu茅s del texto “Foto” y un par de espacios en blanco “&nbsp;” insertamos el elemento ion-icon聽con 聽el icono camera.

Despu茅s tenemos un campo de tipo ion-textarea聽donde podremos introducir la descripci贸n del sitio:

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

Por 煤ltimo tenemos un bot贸n de tipo submit para enviar el formulario. Recordad que cuando pulsemos en este bot贸n se disparar谩 el evento ngSubmit聽y por lo tanto se ejecutar谩 la funci贸n guardarSitio聽tal y como hemos definido en la etiqueta form.

Utilizando la c谩mara del tel茅fono m贸vil

Ahora vamos a ver como sacar una foto y para que se muestre en la etiqueta img que hemos creado:

Para poder utilizar la c谩mara del m贸vil tenemos que instalar el plugin cordova-plugin-camera con el siguiente comando:

ionic cordova plugin add cordova-plugin-camera --save
npm install --save @ionic-native/camera

Ahora el controlador del modal (modal-nuevo-sitio.ts) tenemos聽聽que importar Camera desde ionic-native聽e inyectarlo en el constructor:

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

declare var google:any;
/**
聽* Generated class for the ModalNuevoSitio page.
聽*
聽* See http://ionicframework.com/docs/components/#navigation for more info
聽* on Ionic pages and navigation.
*/

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

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

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

Tambi茅n debemos importar Camera en聽app.module.ts聽y declararlo como provider:

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 { Geolocation } from '@ionic-native/geolocation';
import { Camera } from '@ionic-native/camera';

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


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

Ahora vamos a crear聽el m茅todo sacarFoto en聽en el controlador (modal-nuevo-sitio.ts)聽que se ejecutar谩 al pulsar sobre el bot贸n Foto.

En el archivo modal-nuevo-sitio.ts聽a帽adimos el siguiente c贸digo dentro del controlador:

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);
    });
  }

Para sacar una foto utilizamos el m茅todo getPicture聽pas谩ndole un array de opciones. En las opciones definimos las caracter铆sticas que va a tener la imagen:

  • encodingType:聽Selecciona la codificaci贸n del archivo de imagen devuelto, puede ser JPEG o PNG.
  • targetWidth:聽Anchura de la foto.
  • targetHeight:聽Altura de la foto.
  • destinationType: Define el formato del valor devuelto, puede ser :
    • DATA_URL devuelve la imagen como una cadena codificada en base64.
    • FILE_URI: Crea un archivo con la imagen y devuelve la ruta al archivo.
    • NATIVE_URI: devuelve la ruta nativa al archivo (assets-library:// en iOS o content:// en Android).
  • sourceType: Indica el origen de la foto, puede ser:
    • CAMERA (por defecto).
    • PHOTOLIBRARY
    • SAVEDPHOTOALBUM
  • correctOrientation: Gira la imagen para corregir la orientaci贸n del dispositivo durante la captura.

Puedes visitar el siguiente enlace para conocer聽todas las opciones posibles y saber m谩s sobre el plugin Camera:

https://ionicframework.com/docs/native/camera/

En this.foto聽guardamos la imagen聽codificada en formato base64 que recibimos de la c谩mara, para poder mostrarla como parte de la url en el par谩metro src de la imagen tenemos que a帽adirle聽“data:image/jpeg;base64,”聽por delante.

Por si alguno se ha perdido muestro el contenido completo de como tiene que quedar en estos momentos el archivo聽modal-nuevo-sitio.ts:

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

declare var google:any;

/*
聽聽Generated class for the ModalNuevoSitio page.

聽聽See http://ionicframework.com/docs/v2/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 ) {}

  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);
    });
  }

}

A帽adiendo plataformas

Bien, hasta ahora hemos estado probando nuestra aplicaci贸n en el navegador, sin embargo no podemos probar el plugin camera desde el navegador. Ha llegado la hora de probar nuestra aplicaci贸n en un dispositivo m贸vil.

Lo primero que tenemos que hacer es a帽adir la plataforma en la que queremos probar nuestra aplicaci贸n.

Para a帽adir una plataforma utilizamos el siguiente comando desde consola, recordad que debemos esta siempre dentro de la ios
ionic platform add wp

Por lo tanto si queremos probar nuestra app en android escribiremos:

ionic cordova platform add android

Esto nos crear谩 una carpeta llamada platforms si no estaba creada, y a帽adir谩 una carpeta android con todo el c贸digo necesario para poder generar un archivo apk instalable.

En Mac y Linux tal vez os pida que escrib谩is聽sudo por delante.

Ejecutando nuestra app en el dispositivo m贸vil

Una vez tenemos a帽adida la plataforma si enchufamos nuestro m贸vil con un cable usb a nuestro pc podemos ejecutar la app directamente en el dispositivo con el siguiente comando:

ionic cordova run android

Si no dispon茅is de un dispositivo tambi茅n pod茅is emular la aplicaci贸n utilizando ionic emulate:

ionic cordova emulate android

En ios tambi茅n puedes entrar en la carpeta 聽platforms/ios y abrir el archivo con extensi贸n .xcodeproj desde聽Xcode y ejecutarlo o emularlo聽desde Xcode.

El el m贸vil la app con el modal abierto se ver铆a algo as铆:

Nuestra App funcionando en un dispositivo m贸vil.
Nuestra App funcionando en un dispositivo m贸vil.

Como pod茅is observar mi gato se ha prestado voluntariamente para posar en la foto ;-P

Dependiendo del sistema operativo que utilic茅is en vuestro pc y del dispositivo puede que teng谩is alg煤n 聽problema para que os reconozca el m贸vil, yo no tengo tiempo de investigar cada caso pero googleando seguro que dais con la soluci贸n, 聽os animo a que dej茅is en los comentarios si ten茅is alg煤n problema y que qui茅n encuentre la soluci贸n lo ponga para ayudarnos unos a otros.

Por hoy lo dejamos aqu铆, en el siguiente capitulo veremos como guardar nuestros sitios en una base de datos local en nuestro dispositivo.

 

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 3: A帽adiendo FAB, marcador y ventana modal.

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 insertar en nuestra app un mapa de google maps centrado en las coordenadas actuales.
Vamos a continuar desarrollando nuestra app.

Para poder apreciar mejor donde estamos situados vamos a mostrar un marcador personalizado en el mapa que nos indique nuestra posici贸n.

Para el marcador vamos a utilizar una imagen personalizada. La forma correcta de utilizar im谩genes locales en nuestra app es alojarlas en la carpeta src/assets, as铆 que vamos a crear dentro de src/assets una carpeta la la que llamaremos img聽donde alojaremos nuestras im谩genes.

Podemos poner la imagen que queramos como 聽marcador para el mapa. Si no os quer茅is complicar pod茅is dar bot贸n derecho sobre la siguiente imagen y descargarla para utilizarla en este ejemplo:

Bien, una vez descargada la imagen la copiamos en la carpeta img que acabamos de crear.

Ahora para situar el marcador en el mapa editamos el archivo inicio.ts聽y en la funci贸n聽loadMap()聽que muestra el mapa a帽adimos el siguiente c贸digo:

loadMap(){

   let mapContainer = document.getElementById('map');
    this.map = new google.maps.Map(mapContainer, {
      center: this.coords,
      zoom: 12
    });
    
    // Colocamos el marcador
    let miMarker = new google.maps.Marker({
              icon : 'assets/img/ico_estoy_aqui.png',
              map: this.map,
              position: this.coords
          });

}

No hay mucho que comentar, como veis para crear un marcador creamos un objeto google.maps.Marker 聽y le pasamos un objeto como par谩metro donde en icon le indicamos donde se aloja la imagen del icono, en este caso la ruta a la imagen que hemos guardado destro de assets en la carpeta img es ‘assets/img/ico_estoy_aqui.png’, si vuestra imagen se llama de otra forma ten茅is l贸gicamente que poner el nombre de la imagen que vay谩is a utilizar.
En map聽le idicamos la variable que contiene el mapa donde se tiene que situar, es este caso this.map, por ultimo en position聽le asignamos las coordenadas donde se tiene que situar, en este caso le pasamos la variable this.coords que contiene las coordenadas actuales.

Ahora si ejecutamos ionic serve -l veremos en el navegador algo como esto:

Mostrando un marcador en el mapa
Mostrando un marcador en el mapa

Ahora vamos a dar un paso m谩s y vamos a a帽adir un聽FAB (Floating Action Button) es decir bot贸n de acci贸n flotante al mapa.Los FAB son componentes est谩ndar de material design, tienen la forma de un c铆rculo y flotan聽sobre el contenido en una posici贸n fija.

Este FAB lo utilizaremos para a帽adir la posici贸n actual a nuestros sitios, para ello haremos que cuando se pulse en el fav se habra una ventana modal donde mostraremos un peque帽o formulario donde aparecer谩n las coordenadas y la direcci贸n de la posici贸n actual y nos permitir谩 a帽adir una descripci贸n y una fotograf铆a desde la c谩mara de nuestro m贸vil.

Vayamos por partes.

Primero vamos a colocar el FAB en la vista de muestra p谩gina de inicio, editamos el archivo inicio.html y a帽adimos lo聽que esta marcado聽con fondo amarillo.

<!--
聽聽Generated template for the Inicio page.

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

  <ion-navbar>
    <ion-title>Inicio</ion-title>
  </ion-navbar>

</ion-header>

<ion-content padding>
  <div id="map"></div>
   <ion-fab right top>
    <button ion-fab (tap)="nuevoSitio()">
      <ion-icon name="pin"></ion-icon>
      <ion-icon name="add"></ion-icon>
    </button>
  </ion-fab>
  </ion-content>

Bien, como vemos tenemos el componente ion-fab al que le indicamos que se sit煤e arriba a la derecha con聽right聽y聽top.

En su interior contiene un bot贸n al que le tenemos que indicar que es del tipo ion-fab.

Con (tap)=”nuevoSitio()”聽le indicamos que cuando se pulse o tape el bot贸n se llame a la funci贸n nuevoSitio()聽que聽definiremos luego en el controlador de la p谩gina.

Despu茅s tenemos dos componentes ion-icon聽para mostrar los iconos en el FAB, uno con el icono pin y otro con el icono add, lo habitual es mostrar un solo icono, pero he querido poner dos para que quede m谩s claro que queremos a帽adir una localizaci贸n.

Si probamos ahora nuestra app tendr谩 un aspecto similar a este:

Bien, ahora vamos a definir la funci贸n聽nuevoSitio()聽en el controlador, editamos el archivo inicio.ts y a帽adimos la siguiente funci贸n debajo de la funci贸n loadMap:

nuevoSitio(){
 // aqu铆 vamos a abrir el modal para a帽adir nuestro sitio.
}

La idea es que al llamar a esta funci贸n desde el FAB se abra un modal para poder a帽adir una descripci贸n y una foto a muestro sitio si lo deseamos, vamos a explicar un poco que son los modales y como se utilizan:

Modales

Los modales son como ventanas que se abren dentro de nuestra aplicaci贸n sin que afecten a la pila de navegaci贸n.

Para crear un modal debemos de crear una p谩gina con el ionic generator.
Desde consola vamos a escribir el siguiente comando para crear el modal donde ir谩 el formulario para introducir el nuevo sitio:

ionic g page modalNuevoSitio

Ahora en inicio.ts debemos importar el componente聽ModalController聽 de la librer铆a ionic-angular, por lo tanto despu茅s de NavController y NavParams importamos tambi茅n ModalController.

import { NavController, NavParams, ModalController } from 'ionic-angular';

Debemos inyectar tambi茅n en el constructor el componente ModalController al que hemos llamado modalCtrl:

constructor(
    public navCtrl: NavController,
    public navParams: NavParams,
    private geolocation: Geolocation,
    public modalCtrl : ModalController,
    platform: Platform) {

    ......

Ahora ya estamos listos para crear el modal en nuestra funci贸n nuevoSitio.

Para crear un modal utilizamos el m茅todo聽create del componente ModalController聽y le pasamos como primer par谩metro un string con el nombre del 聽controlador de la p谩gina que hemos creado para el modal llamado聽ModalNuevoSitioPage.
El segundo par谩metro es opcional y se utiliza para pasarle datos a nuestro modal, en este caso lo vamos a utilizar para pasarle el objeto聽this.coords que contiene las coordenadas que hemos obtenido.

Una vez creado el modal para que se muestre en pantalla invocamos al m茅todo present();

nuevoSitio(){
  // aqu铆 vamos a abrir el modal para a帽adir nuestro sitio.
   let mimodal = this.modalCtrl.create( 'ModalNuevoSitioPage',this.coords );
   mimodal.present();
}

Ahora que sabemos como mostrar un modal y como pasarle datos desde la p谩gina que lo llama, vamos a ver como recibimos esos datos y los mostramos en el modal.

Recordad que al usar lazy loading y declarar la p谩gina del modal en su propio modulo (inicio.module.ts)no debemos declararlo en app.module.ts.

Vamos a crear una variable 聽en el controlador del modal (modal-nuevo-sitio.ts)聽que al igual que en la p谩gina inicio llamaremos coords y sera de tipo any, esta variable al igual que en la p谩gina de inicio va a contener un objeto con la latitud y longitud que recibimos en la llamada.

Para recibir los datos desde la p谩gina que llama al modal solo tenemos que utilizar el m茅todo聽get聽de NavParams聽que ya se importa por defecto al crear una p谩gina. Con navParams controlamos los par谩metros que recibimos.
Dentro de la funci贸n聽ionViewDidLoad聽que se ejecuta al cargar la vista vamos a asignar a cada atributo聽del objeto coords聽 que acabamos de crear el valor que corresponde de la latitud y longitud que recibimos.
Veamos como tiene que quedar el c贸digo de modal-nuevo-sitio.ts en estos momentos:

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


/*
聽聽Generated class for the ModalNuevoSitio page.

聽聽See http://ionicframework.com/docs/v2/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 }

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

  ionViewDidLoad() {
    console.log('ionViewDidLoad ModalNuevoSitioPage');
    this.coords.lat = this.navParams.get('lat');
    this.coords.lng = this.navParams.get('lng');
  }
}

Ahora que hemos recibido las coordenadas desde la p谩gina inicio vamos a mostrarlas en el modal para comprobar que las recibimos correctamente, para ello vamos a editar el archivo聽modal-nuevo-sitio.html聽y simplemente a帽adimos las coordenadas dentro de ion-content:

<ion-content padding>
  <p>Hemos recibido: {{ coords.lat }}, {{ coords.lng }}<p>
</ion-content>

Si probamos ahora muestra aplicaci贸n observamos que al pulsar el bot贸n (FAB) que hemos creado para a帽adir sitios nos abre una ventana modal donde se muestran las coordenadas que acabamos de recibir, pero tenemos un peque帽o problema, y es que no tenemos forma de cerrar聽el modal, as铆 que antes de editar cualquier otra cosa en la p谩gina vamos a crear un bot贸n de cerrar.

Para cerrar el modal tenemos que importar en el controlador de la pagina del modal (modal-nuevo-sitio.ts) el controlador ViewController e inyectarlo en el constructor.

Luego creamos una funci贸n que vamos a llamar cerrarModal donde utilizaremos 聽el m茅todo聽dismiss de ViewController para cerrarlo:

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams,  ViewController } from 'ionic-angular';


/*
聽聽Generated class for the ModalNuevoSitio page.

聽聽See http://ionicframework.com/docs/v2/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 }
  constructor(public navCtrl: NavController, public navParams: NavParams, private viewCtrl : ViewController ) {}

  ionViewDidLoad() {
    console.log('ionViewDidLoad ModalNuevoSitioPage');
    this.coords.lat = this.navParams.get('lat');
    this.coords.lng = this.navParams.get('lng');
  }

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

}

Ahora vamos a a帽adir el bot贸n de cerrar que llamar谩 a la funci贸n cerrarModal que acabamos de crear 聽en la cabecera de la vista de la p谩gina del modal (modal-nuevo-sitio.html), aprovechamos tambien para cambiar el t铆tulo del modal a “Nuevo sitio”:

<!--
聽聽Generated template for the ModalNuevoSitio page.

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

  <ion-navbar>
    <ion-title>Nuevo sitio</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>
  <p>Hemos recibido: {{ coords.lat }}, {{ coords.lng }}<p>
</ion-content>

Ahora ya tenemos un bot贸n de cerrar para nuestro modal, si hab茅is seguido correctamente todos los pasos al pulsar sobre el FAV聽se聽abrir谩 nuestro modal como en la siguiente imagen:

Por ahora lo dejamos aqu铆, hoy hemos aprendido聽como a帽adir un marcador al mapa de Google maps, como a帽adir a nuestra app botones tipo FAB, y como crear, abrir, pasar datos y cerrar un modal.
En el pr贸ximo post veremos como a帽adir un nuevo 聽con descripci贸n y foto, haciendo uso de la c谩mara del m贸vil.

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 2: Mostrando el mapa.

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

Hola a todos:

En el post anterior creamos una aplicaci贸n con tres tabs y vimos como navegar entre ellas.
Hoy vamos a seguir construyendo la aplicaci贸n.Lo siguiente que vamos a hacer es mostrar un mapa en la p谩gina de inicio centrado en las coordenadas actuales donde nos encontremos.

Lo primero que vamos a necesitar es cagar la librer铆a de Google maps , si quieres saber m谩s sobre la api de google maps聽puedes consultar este tutorial.

Editamos el archivo index.html聽que es la plantilla principal que se carga al iniciar la aplicaci贸n聽y a帽adimos la siguiente linea antes de 聽cargar el script 聽build/main.js:

<script src="https://maps.google.com/maps/api/js"></script>

El c贸digo completo quedar铆a as铆:

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
  <meta charset="UTF-8">
  <title>Ionic App</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <meta name="format-detection" content="telephone=no">
  <meta name="msapplication-tap-highlight" content="no">

  <link rel="icon" type="image/x-icon" href="assets/icon/favicon.ico">
  <link rel="manifest" href="manifest.json">
  <meta name="theme-color" content="#4e8ef7">

  <!-- cordova.js required for cordova apps -->
  <script src="cordova.js"></script>

  <!-- un-comment this code to enable service worker
聽聽
聽聽聽聽if ('serviceWorker' in navigator) {
聽聽聽聽聽聽navigator.serviceWorker.register('service-worker.js')
聽聽聽聽聽聽聽聽.then(() => console.log('service worker installed'))
聽聽聽聽聽聽聽聽.catch(err => console.log('Error', err));
聽聽聽聽}
聽聽-->

  <link href="build/main.css" rel="stylesheet">

</head>
<body>

  <!-- Ionic's root component and where the app will load -->
  <ion-app></ion-app>

  <!-- The polyfills js is generated during the build process -->
  <script src="build/polyfills.js"></script>

   <script  src="https://maps.google.com/maps/api/js"></script>

  <!-- The bundle js is generated during the build process -->
  <script src="build/main.js"></script>

</body>
</html>

Como vemos esta el la p谩gina principal donde se va a cargar todo el contenido de la app dentro de <ion-app></ion-app> que es componente raid de la aplicaci贸n.

Ahora para poder acceder a las coordenadas del m贸vil entra en juego ionic native.

Con ionic native lo que hacemos es instalar un plugin que nos da acceso a alguna caracter铆stica nativa del m贸vil como la c谩mara, el gps, la br煤jula etc a trav茅s de typescript sin tener que programar nosotros en c贸digo nativo (Java para Android, Swift o objetive c para iOS).

Simplemente instalamos el plugin y luego importamos el plugin de ionic-native.

En la documentaci贸n de ionic 聽podemos ver todos los plugins disponibles en ionic native:

http://ionicframework.com/docs/v2/native/

En esta ocasi贸n necesitamos el plugin聽cordova-plugin-geolocation.
Este plugin proporciona informaci贸n sobre la ubicaci贸n del dispositivo, tales como la latitud y la longitud.

Para instalarlo desde el terminar dentro de la carpeta de nuestro proyecto tenemos que ejecutar los聽siguientes comandos:

ionic cordova plugin add cordova-plugin-geolocation --variable GEOLOCATION_USAGE_DESCRIPTION="La app necesita geolocalizaci贸n"
npm install @ionic-native/geolocation --save

Nos sacar谩 un mensaje diciendo que falta instalar el plugin @ionic/cli-plugin-cordova y nos preguntar谩 si queremos instalarlo, le decimos que si.

Si est谩s utilizando Linux o Mac y te sale un error al intentar instalar el plugin prueba a ejecutar el comando con sudo por delante.

Si os muestra un warning como este:

npm WARN ajv-keywords@2.1.0 requires a peer of ajv@>=5.0.0 but none was installed.

Pode铆s solucionarlo escribiendo el siguiente comando:

npm install ajv@latest

Antes de nada debemos declarar el plugin como provider en el archivo app.module.ts, para ello editamos dicho archivo, importamos el plugin Geolocation y lo a帽adimos en la secci贸n providers:

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 { Geolocation } from '@ionic-native/geolocation';


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


@NgModule({
  declarations: [
    MyApp,

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

Bien, ahora vamos a importar el plugin en el controlador de la p谩gina inicio, para ello dentro de la carpeta pages/inicio editamos el archivo inicio.ts聽e importamos el plugin Geolocation de ionic-native.
Tambi茅n vamos a聽importar聽Platform 聽desde ionic-angular para poder acceder al evento ready de Platform聽que se lanza cuando la aplicaci贸n se ha cargado completamente y esta lista. Esto nos evita errores al intentar llamar a un plugin antes de que se haya cargado.

Para importar estos dos m贸dulos incluimos las siguientes lineas en los imports:

import { Geolocation } from '@ionic-native/geolocation';
import { Platform } from 'ionic-angular';

Ahora cuando la aplicaci贸n est茅 completamente cargada y lista (con el evento ready de Platform) vamos a obtener las coordenadas donde nos encontramos y mostrar un mapa centrado en las coordenadas actuales.

Creamos una variable miembro llamada map del tipo any (admite cualquier valor) que contendr谩 el manejador del mapa de google.
Despu茅s para poder utilizar Platform tenemos que inyectarlo como dependencia en el constructor de la clase, tambi茅n debemos inyectar Geolocation en el constructor. En el evento ready de Platform llamaremos a una funci贸n聽que vamos a llamar obtenerPosicion. En estos momentos el c贸digo de inicio.ts deber铆a quedarnos as铆:

import { Component } from '@angular/core';
import { NavController, NavParams } from 'ionic-angular';

import { Geolocation } from '@ionic-native/geolocation';
import { Platform } from 'ionic-angular';


/*
聽聽Generated class for the Inicio page.

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

  map: any; // Manejador del mapa.

  constructor(
    public navCtrl: NavController,
    public navParams: NavParams,
   public  platform: Platform,
   private geolocation: Geolocation){

     platform.ready().then(() => {
      // La plataforma esta lista y ya tenemos acceso a los plugins.
        this.obtenerPosicion();
     });

  }

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

}

Hagamos un peque帽o par茅ntesis en nuestra app para hablar de un par de conceptos que vamos a utilizar con frecuencia en nuestros desarrollos con ionic 2:

Promesas

Vemos que dentro del constructor lamamos al m茅todo ready()聽 de聽platform聽 y despu茅s de un punto utilizamos la palabra then(…).

Esto es lo que llamamos una promesa.

Normalmente los plugins de聽ionic-native聽nos devuelven una promesa

Las promesas las inclu铆a angular 1 pero ahora las promesas ya son nativas en EMACScript 6.

Las promesas vienen a sustituir a los callbacks. Se ejecuta la 聽funci贸n y cuando obtenemos un resultado en el then se ejecuta el c贸digo que necesitemos.

Si falla la ejecuci贸n de alguno podemos utilizar la funci贸n catch para tratar el error.

Funciones de flecha

Otra cosa que te puede resultar rara es que dentro del then en lugar de ejecutar una funci贸n al uso estamos utilizando lo que se denomina funciones de flecha o en ingl茅s (Fat Arrow functions).

La funciones con flecha permiten crear funciones an贸nimas m谩s f谩cilmente y adem谩s permiten utilizar this en el contexto actual.
Vamos a ver聽un poco m谩s en detalle esto con聽
el siguiente ejemplo:

class MiClase {
  constructor() {
    this.nombre = 'Eduardo';
    setTimeout(() => {
      // Esto imprim茅 en la consola "Eduardo" ya que en las funciones con flecha this hace referencia al contexto actual.
      console.log(this.nombre);
    });
  }
}

Esto ser铆a lo mismo que escribir lo siguiente:

class MiClase {
  constructor() {
    this.nombre = 'Eduardo';
    var _this = this;
    setTimeout(function() {
     console.log(_this.nombre);
    });
  }
}

Ahora que ya conocemos lo que son las promesas y las funciones de flecha podemos seguir con nuestra aplicaci贸n.

Antes de nada vamos a a帽adir una聽variable en en controlador 聽que vamos a vamos a llamar聽coords聽y聽en ella guardaremos un objeto con la latitud y longitud donde nos encontramos.

Por lo tanto encima del constructor de la clase despu茅s de la variable map definimos la variable coords:

...

export class Inicio {

聽聽map: any; // Manejador del mapa.
 coords : any = { lat: 0, lng: 0 }

  constructor(

...

Si el editor os marca los errores observareis que os subraya this.obtenerPosicion(), esto es evidentemente porque estamos llamando a una funci贸n que todav铆a no hemos definido.

Vamos a definir la funci贸n obtenerPosici贸n():

obtenerPosicion():any{
    this.geolocation.getCurrentPosition().then(res => {
      this.coords.lat = res.coords.latitude;
      this.coords.lng = res.coords.longitude;

      this.loadMap();
    })
    .catch(
      (error)=>{
        console.log(error);
      }
    );
  }

La funci贸n聽getCurrentPosition()聽del plugin Geolocation nos devuelve una promesa.

Como podemos observar resolvemos la promesa con una funci贸n de flecha. En la funci贸n de flecha recibimos como par谩metro el objeto res. Lo que nos interesa obtener es la longitud y latitud donde nos encontramos, estos valores est谩n en res.coords.longitude y res.coords.latitude.
Asignamos esos valores al objeto de la variable this.cords que acabamos de definir y despu茅s llamamos a la funci贸n this.loadMap()聽que a煤n no hemos creado.

Recordad que debemos de聽utilizar this聽para hacer referencia a聽las variables聽miembro y m茅todos que definamos en la clase del controlador.

Ahora deberemos crear la funci贸n loadMap聽que se encargar谩 de mostrar un mapa en la p谩gina centrado en las coordenadas que hemos recogido.

Antes de definir la funci贸n necesitamos crear el contenedor donde se va a mostrar el mapa en la vista. La librer铆a javascript de Google maps lo que hace es insertar un mapa en un div, por lo que necesitamos crear ese div en la vista y luego pas谩rselo como referencia para crear el mapa.

Editamos la vista de la p谩gina inicio, es decir el archivo inicio.html聽y a帽adimos un div dentro de ion-content 聽al que le asignamos como id “map”:

<!--
聽聽Generated template for the Inicio page.

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

  <ion-navbar>
    <ion-title>Inicio</ion-title>
  </ion-navbar>

</ion-header>


<ion-content padding>

  <div id="map"></div>

</ion-content>

Ahora tenemos que asignarle un tama帽o al mapa as铆 que por primera vez vamos a editar el archivo de estilos de una p谩gina, en este caso vamos a editar el archivo inicio.scss.

La extensi贸n del archivo .scss, hace referencia a que es una archivo Sass, sus siglas hacen referencia a (Syntactically Awesome Stylesheets) algo as铆 como “Hoja de estilo sint谩cticamente impresionante”.

Sass聽es un lenguaje de hoja de estilos que extiende el css tradicional proveyendo de varios mecanismos que est谩n presentes en los lenguaje de programaci贸n. Con Sass puedes utilizar variables,聽c贸digo anidado, mixins, etc.

Ionic ya viene con Sass instalado lo que hace realmente f谩cil su utilizaci贸n. Sass es un lenguaje de script que es traducido a css. Podemos a帽adir reglas聽de estilo de igual manera que lo hacemos con css por lo que de momento para definir el tama帽o del mapa no necesitamos saber mas. Si quieres m谩s informaci贸n sobre Sass puede consultar la documentaci贸n oficial:聽http://sass-lang.com/documentation/

Veamos como tiene que quedar el archivo inicio.scss:

page-inicio {
ion-content{
聽聽聽聽#map {
聽聽聽聽聽聽width: 100%;
聽聽聽聽聽聽height: 100%;
    }
  }
}

Como podemos ver聽dentro del elemento page-inicio definimos el estilo para ion-content聽 y a su vez dentro definimos el estilo para #map al que le estamos diciendo que ocupe todo al ancho y el alto de la p谩gina, como vemos Sass nos permite anidar los elementos.

Bien, una vez hechos estos preparativos ya podemos definir la funci贸n loadMap que se encargar谩 de mostrar un mapa en la pagina centrado en las coordenadas que hemos recogido. Para que聽Typescript no de error por no reconocer la clase google cuando la llamemos desde la funci贸n que vamos a crear, vamos a declarar la variable google justo debajo de los imports con declare聽var聽google:聽any;聽. Veamos como tiene que quedar el c贸digo de聽inicio.ts聽con la variable google definida y nuestra funci贸n loadMap:

import { Component } from '@angular/core';
import { NavController, NavParams } from 'ionic-angular';
import { Geolocation } from '@ionic-native/geolocation';
import { Platform } from 'ionic-angular';

declare var google: any;



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

 map: any; // Manejador del mapa.
 coords : any = { lat: 0, lng: 0 }

  constructor(public navCtrl: NavController, public navParams: NavParams,private geolocation: Geolocation,public  platform: Platform) {
    platform.ready().then(() => {
      // La plataforma esta lista y ya tenemos acceso a los plugins.
        this.obtenerPosicion();
     });

  }

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

  loadMap(){
   let mapContainer = document.getElementById('map');
    this.map = new google.maps.Map(mapContainer, {
      center: this.coords,
      zoom: 12
    });
}

 obtenerPosicion():any{
    this.geolocation.getCurrentPosition().then(res => {
      this.coords.lat = res.coords.latitude;
      this.coords.lng = res.coords.longitude;

      this.loadMap();
    })
    .catch(
      (error)=>{
        console.log(error);
      }
    );
  }
}

En la funci贸n loadMap()聽asignamos a la variable mapContainer聽el elemento div con id=”map” que hab铆amos creado en la vista y creamos un mapa con google.maps.Map聽donde le pasamos dos par谩metros, el primero es el elemento contenedor del mapa que lo hemos recogido en la variable mapContainer聽y el segundo par谩metro es un objeto donde le pasamos la configuraci贸n del mapa, en este caso le pasamos las coordenadas al par谩metro center聽y le decimos que muestre el mapa con un zoom de 12.

En este punto si probamos nuestra aplicaci贸n en el navegador con ionic serve -l deber铆amos ver algo como esto:

Pantalla de Inicio con nuestro mapa
Pantalla de Inicio con nuestro mapa

Para no hacer demasiado largo este post lo vamos a dejar aqu铆.

En el siguiente post seguiremos desarrollando la app, veremos como a帽adir un bot贸n FAB, como a帽adir un marcador al mapa y como crear una ventana modal y pasarle datos desde el controlador de la p谩gina que hace la llamada.

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