Archivo de la etiqueta: libro ionic 2

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

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

 

¬°¬°Atenci√≥n!! este tutorial se basa en ionic 3 y est√° desactualizado por lo que es posible que los ejemplos no funcionen en la √ļltima versi√≥n de ionic, haz click aqu√≠ para acceder a un tutorial mas actual de Ionic.

Hola a todos:

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

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

Provider

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

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

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

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

ionic g provider db

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

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

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

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

/*
  Generated class for the Db provider.

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

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

}

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

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

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

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

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

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

/*
  Generated class for the Db provider.

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

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

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

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

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

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

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

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

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

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

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

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

Para poder utilizar el provider¬† y SQLite debemos importar ambos en¬†app.module.ts¬†y declararlos en la secci√≥n providers, ionic generator nos ha a√Īadido autom√°ticamente nuestro provider DbProvider en app.module.ts as√≠ que solo tenemos que preocuparnos de importar y declarar Sqlite:

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

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

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


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

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

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

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

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

    });
  }
}

Por √ļltimo importamos el provider en la p√°gina donde vamos a utilizarlo, en este caso en modal-nuevo-sitio.ts,¬†tambi√©n¬†debemos inyectarlo en el constructor :

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



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

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

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

...

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

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

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

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

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

Si todo ha ido bien cerramos el modal.

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

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

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

/*
  Generated class for the Db provider.

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

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

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

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

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

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

}

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

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


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

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

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

...

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

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

Ciclo de vida de una p√°gina

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

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

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

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

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

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

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

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

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

A√Īadimos el m√©todo ionViewDidEnter al controlador de la p√°gina listado en el archivo listado.ts¬†y hacemos¬†la llamada para extraer nuestros sitios de la base de datos:

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


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

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

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

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

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

}

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

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

Ahora solo nos queda mostrarlos en el listado:

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

<!--
  Generated template for the Listado page.

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

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

</ion-header>

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

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

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

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

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

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

 

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

ionic cordova run android

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

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

 

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

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:

Tutorial de Ionic РCrear una aplicación para guardar nuestros sitios geolocalizados РParte 1 РNavegación por Tabs

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 cap√≠tulo anterior de este tutorial sobre Ionic 2 creamos una app simple, un minijuego de adivinar n√ļmeros que nos sirvi√≥ para prender c√≥mo se programa la l√≥gica de una p√°gina en Ionic 2.

Vimos cómo pasar variables entre el controlador de la página y la vista y cómo llamar a funciones desde un evento de la vista como pulsar un botón.

La aplicación era extremadamente sencilla y toda la lógica se desarrollaba en la misma página, sin embargo lo normal en cualquier aplicación que sea mínimamente completa es que tenga varias vistas o páginas y haya que navegar entre ellas.

Hoy vamos a aprender a hacer una aplicación con varias páginas y veremos cómo podemos navegar entre ellas, para ello vamos a realizar una aplicación de ejemplo.

Descripción de la aplicación:

La aplicación consistirá en una herramienta que nos permita guardar el lugar donde nos encontramos actualmente recogiendo las coordenadas gracias al gps del móvil.

Est√° aplicaci√≥n puede sernos √ļtil por ejemplo para recordar donde hemos aparcado el coche o para guardar un restaurante que nos ha gustado y queremos volver m√°s tarde etc.

La aplicaci√≥n constar√° de una pantalla inicial con un mapa donde se mostrar√° la posici√≥n actual y un bot√≥n para a√Īadir la posici√≥n actual. Al pulsar el bot√≥n se abrir√° una ventana modal con un peque√Īo formulario donde a√Īadir una descripci√≥n y un fotograf√≠a.

Los lugares que guardemos se mostrar√°n en otra p√°gina que contendr√° un listado de tus sitios guardados.

Al pinchar sobre uno de nuestros sitios guardados se abrirá otra ventana modal donde mostraremos la foto, la dirección y la descripción del mismo.

Bien, vamos a comenzar creando nuestra aplicación. Desde la consola de comandos o terminal escribimos:

ionic start misSitios blank

Una vez creada nuestro proyecto vemos que por defecto nos ha generado la p√°gina home dentro de la carpeta pages.

Nosotros a priori vamos a utilizar tres páginas, una con el mapa donde se muestra nuestra posición actual, otra con el listado de nuestros sitios guardados y otra que llamaremos info donde mostraremos información sobre la aplicación y que simplemente utilizaremos de relleno para tener una tercera página y poder mostrar mejor como funciona la navegación entre páginas.

Las paginas normalmente suelen estar asociadas a “tabs” o pesta√Īas y se navega entre ellas cambiando de tab.

Ionic Generator

Nosotros podemos crear las páginas a mano creando una carpeta dentro de la carpeta pages con su vista html y su controlador .ts, y su css, también podemos crear y configurar los tabs a mano, pero el cli (command line interface o interfaz de linea de comandos) de ionic nos facilita muchísimo el trabajo. Ionic dispone de una herramienta llamada ionic generator.

Ionic generator nos permite generar plantillas con los componentes que queramos.

Con el siguiente comando obtenemos la lista de elementos disponible que podemos generar con ionic generator:

ionic g --list

Recordad que debemos estar dentro de la carpeta del proyecto para poder ejecutar cualquier comando de ionic, por lo que deberemos escribir cd misSitios desde consola para entrar dentro de la carpeta de nuestro proyecto.

La lista de elements que podemos generar autom√°ticamente con ionic generator son:

  • Component: Los componentes son un conjunto de html, con su css y su comportamiento que podemos reutilizar en cualquier lugar sin tener que reescribir de nuevo todo.
  • Directive:
    Una directiva sirve para modificar atributos de un elemento.
  • Page: P√°ginas.
  • Pipe: Los pipes son lo que angular 1 se consideraban filtros.
  • Provider:Los providers son proveedores que se encargan del manejo de datos, bien extra√≠dos de la base de datos, desde una api, etc.
  • Tabs: Nos genera la p√°gina maestra de Tabs y una p√°gina para cada tab.

Veremos con m√°s detalle cada elemento seg√ļn lo vayamos necesitando, para este ejemplo de momento nos interesan los Tabs.

Seleccionamos Tabs y como  nombre le ponemos misTabs, también podemos generar directamente los tabs con el siguiente comando:

ionic g tabs misTabs

Al ejecutar el comando primero nos pregunta el n√ļmero de tabs que queremos crear, para este ejemplo vamos a utilizar 3.Utilizamos la flecha hacia abajo del cursor para seleccionar el tres y pulsamos enter.

Acto seguido debemos introducir el nombre del primer tab: le damos el nombre de Inicio y pulsamos enter.

Después nos pide que introduzcamos el nombre para el segundo Tab: A este tab le vamos a llamar Listado.

Pulsamos enter una vez mas y por ultimo introducimos el nombre del tercer tab. Le llamamos Info y pulsamos enter.

captura ionic generator tabs

Hay que tener cuidado al utilizar el comando ionic g tabs porque si alguna de las p√°ginas que creamos con ionic g tabs ya existe la “machaca” y la crea de nuevo vac√≠a.

Si echamos un vistazo a las carpetas de nuestro proyecto vemos que en la carpeta /src/pages se han creado cuatro p√°ginas nuevas, una por cada uno de los tabs que acabamos de crear y una cuarta llamada mis-tabs que sera la p√°gina maestra de los tabs.

 

captura-de-pantalla-2017-01-28-a-las-15-57-22

Desde la versión 3 de ionic al crear una página con ionic generator además del archivo nombre_pagina.ts también se genera un archivo nombre_pagina.module.ts en cada página generada, este archivo se utiliza para poder realizar lo que se conoce como Lazy Loading, que permite cargar paginas y recursos bajo demanda acelerando así la carga de las páginas.

Esta nueva característica es opcional, si no queremos utilizarla, para que no de error deberiamos eliminar el archivo xxx.module.ts de todas las páginas.

Para poder realizar lazy loading necesitamos importar IonicPage junto con NavController de ionic-angular e incluir el decorador @IonicPage() en cada página. El decorador @IonicPage() nos permite definir los deep links directamente en el componente, esto lo hace automáticamente ionic generator al crear una página.

Como a partir de ahora vamos a utilizar lazy loading, si ionic generator no lo genera autom√°ticamente, tendremos que crear estos elementos a mano.

Si abrimos el archivo mis-tabs.module.ts vemos que tiene el siguiente  código:

import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { MisTabsPage } from './mis-tabs';

@NgModule({
  declarations: [
    MisTabsPage,
  ],
  imports: [
    IonicPageModule.forChild(MisTabsPage),
  ],
  exports: [
    MisTabsPage
  ]
})
export class MisTabsPageModule {}

Como vemos  importa NgModule, IonicPageModule y la propia página, en este caso MisTabsPage, después en @NgModule se declara MisTabsPage en declarations, en  imports con IonicPageModule.forchild(MisTabsPage) y en exports.

Si abrimos el archivo mis-tabs.ts dentro de carpeta mis tabs vemos el siguiente código:

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

/**
 * Generated class for the MisTabsPage tabs.
 *
 * See https://angular.io/docs/ts/latest/guide/dependency-injection.html for
 * more info on providers and Angular DI.
 */
@Component({
  selector: 'page-mis-tabs',
  templateUrl: 'mis-tabs.html'
})
@IonicPage()
export class MisTabsPage {

  inicioRoot = 'InicioPage'
  listadoRoot = 'ListadoPage'
  infoRoot = 'InfoPage'


  constructor(public navCtrl: NavController) {}

}

Esta es la pagina maestra donde vamos a controlar la navegación por tabs.

Vemos que ha creado una variable para cada tab (inicioRoot,listadoRoot,¬†e¬†infoRoot) y les ha asignado el controlador de una p√°gina a cada uno entre comillas (‘). Al realizar Lazy loading no necesitamos importar las p√°ginas ni declararlas en app.module.ts,¬†simplemente se declaran en su propio archivo de modulo y se hace referencia con el nombre de la clase como string, por eso hay que ponerlo entre comillas.

Bien, si ejecutamos ionic seve -l para ver e resultado ahora nos muestra la página home que se crea por defecto al crear un proyecto ionic vacío, tenemos que indicarle a la aplicación que la página inicial debe ser la página maestra con los tabs que acabamos de crear.

Cambiar el root de nuestra aplicación (Página principal)

Para indicarle a la aplicación que la página principal sea la página maestra de los tabs que acabamos de crear debemos editar el archivo src/app/app.component.ts e importar la pagina MisTabsPage con import y luego a la variable rootPage asignarle el nombre de la clase de la página que queremos asignar como página principal, en este caso MisTabsPage.

Dado que nuestro componente¬†MisTabsPage est√° ahora cargado utilizando lazy loaded, no queremos importarlo directamente y hacer referencia a √©l en cualquier lugar. En su lugar, podemos pasar una cadena que coincida con el componente por lo tanto¬†debemos asignar a rootPage la cadena¬†‘MisTabsPage’ entre comillas, es decir debemos sustituir:

rootPage = HomePage;

Por:

rootPage = 'MisTabsPage';

El código del archivo src/app/app.component.ts completo quedaría así:

import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar, Splashscreen } from 'ionic-native';

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

  constructor(platform: Platform) {
    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();
    });
  }
}

El import de la página home ya no lo necesitamos así que podemos eliminarlo. De hecho podemos eliminar la carpeta de la pagina home ya que no la vamos a utilizar en esta aplicación, eso sí, antes de borrar la carpeta de la página home elimina también el import y las declaraciones de homePage en el archivo app.module.ts.

El archivo app.component.ts es el punto de entrada de nuestra aplicación, vemos que se define como un componente que utiliza como plantilla el archivo app.html.

@Component({
  templateUrl: 'app.html'
})

Si vemos el contenido de app.html podemos observar que √ļnicamente contiene la siguiente linea de c√≥digo:

<ion-nav [root]="rootPage"></ion-nav>

Le asigna como root (es decir como página inicial o raíz) la variable rootPage, y como a la variable rootPage le hemos asignado la página MisTabsPage, la aplicación cargará dentro de <ion-nav> el contenido de la página MisTabsPage.

De momento no necesitamos modificar nada mas en app.component.ts, solo comentar que por defecto se importa el elemento platform que tiene el método ready que podemos utilizar para ejecutar acciones que queremos que se produzcan en el momento que la aplicación esté completamente cargada:

platform.ready().then(() => { ... }

De momento lo dejamos como est√°.

Ahora ya podemos ejecutar nuestra aplicaci√≥n y ver las tres pesta√Īas que nos ha creado:

Nuestra app con tabs
Nuestra app con tabs

Como vemos ha creado por defecto un icono para cada pesta√Īa.

Vamos a ver como podemos cambiar los iconos de los tabs para ello vamos ahora a ver el contenido de la plantilla mis-tabs.html:

<ion-tabs>
    <ion-tab [root]="inicioRoot" tabTitle="Inicio" tabIcon="information-circle"></ion-tab>
    <ion-tab [root]="listadoRoot" tabTitle="Listado" tabIcon="information-circle"></ion-tab>
    <ion-tab [root]="infoRoot" tabTitle="Info" tabIcon="information-circle"></ion-tab>
</ion-tabs>

Como podemos observar la página maestra de los tabs (mis-tabs.html) solamente contiene el contenedor de tabs que se define con la etiqueta <ion-tabs> y luego una etiqueta <ion-tab> por cada tab. También podemos observar que cada tab tiene el atributo [root] donde se le asigna la variable que indica la pagina que se debe mostrar en cada tab.

Luego tenemos el atributo tabTitle al que se le asigna el texto que se muestra en el tab, si solo queremos que se muestre el icono podemos eliminar o dejar vacío este atributo.

Y por √ļltimo tenemos el atributo tabIcon y es aqu√≠ donde se le asigna el icono que se muestra en cada tab, si solo queremos que se muestre texto podemos eliminar o dejar vac√≠o este atributo.
Si corres la aplicación en el navegador con ionic serve -l puedes observar que en iOS los iconos son diferentes a los iconos de Android. Los iconos cambian dependiendo de la plataforma móvil adaptandose al estilo propio de la plataforma. Esta es una de las ventajas de ionic a partir de la versión 2, que no te tienes que preocupar por adaptar el estilo que tu app para cada plataforma ya que la mayoría los elementos se adaptan automáticamente al estilo propio de cada sistema.

Probablemente te estar√°s preguntando, pero…¬Ņde donde salen estos iconos?.La respuesta es simple, ionic viene de serie con una colecci√≥n de los iconos mas comunes lista para ser usada solamente con asignar como hemos visto el nombre del icono al atributo tabIcon.

El listado de iconos disponibles lo podéis consultar el la documentación oficial de ionic desde el siguiente enlace: https://ionicframework.com/docs/ionicons/

El tab Inicio lo vamos a dejar con el icono home, al tab Listado le vamos a asignar el icono list-box y al tab Info le vamos a asignar el icono information-circle:

<ion-tabs>
  <ion-tab [root]="inicioRoot" tabTitle="Inicio" tabIcon="home"></ion-tab>
  <ion-tab [root]="listadoRoot" tabTitle="Listado" tabIcon="list-box"></ion-tab>
  <ion-tab [root]="infoRoot" tabTitle="Info" tabIcon="information-circle"></ion-tab>
</ion-tabs>

Para observar mejor como cambiamos entre las distintas p√°ginas de momento vamos a poner un comentario en cada una.

Editamos inicio.html y dentro de ion-content a√Īadimos lo siguiente:

<!--
  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>
  <h1>Esta es l√° p√°gina de inicio, aqu√≠ situaremos un mapa con un bot√≥n de a√Īadir lugar.</h1>
</ion-content>

Editamos listado.html y a√Īadimos los siguiente:

<!--
  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>
  <h1>Aquí estará el listado de sitios guardados.</h1>
</ion-content>

Y por √ļltimo editamos info.html dej√°ndolo de la siguiente manera:

<!--
  Generated template for the Info page.

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

  <ion-navbar>
    <ion-title>Info</ion-title>
  </ion-navbar>

</ion-header>


<ion-content padding>
<h1>Mis sitios:</h1>
<p>Aplicaci√≥n de muestra del Tutorial de Ionic 2 ‚Äď Construye Apps m√≥viles multiplataforma con ionic 2 desde cero.</p>
</ion-content>

Ahora al navegar por las pesta√Īas podemos ver algo parecido a esto:

Nuestras tres pesta√Īas en funcionamiento-.
Nuestras tres pesta√Īas en funcionamiento.

Como no quiero hacer posts demasiado largos de momento lo dejamos aquí. Hoy hemos aprendido a crear una aplicación con tabs para navegar entre diferentes paginas usando el comando ionic g tabs.

En el próximo post seguiremos creando nuestra aplicación para guardar sitios. Insertaremos un mapa en la página de inicio y capturaremos las coordenadas actuales.

 

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

Tutorial de Ionic – Mini Juego de acertar n√ļmeros en ionic 2, el controlador de la p√°gina y Data Binding y *ngIF

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:

Despu√©s de ver en el capitulo anterior c√≥mo modificar la plantilla de una p√°gina para simplemente mostrar el texto “Hola Mundo”, hoy vamos a avanzar un poco m√°s en nuestro aprendizaje¬†y vamos a programar nuestra primera aplicaci√≥n interactiva.

Se trata de un simple juego de acertar el n√ļmero secreto. La aplicaci√≥n crear√° un n√ļmero aleatorio que nosotros debemos adivinar.

Por primera vez vamos a programar con ionic 2. Por el camino aprenderemos conceptos como Data Binding, que nos permite actualizar valores entre el html y el controlador y recoger las acciones del usuario como  pulsar un botón.

Bien, sin más preámbulos  vamos a empezar:

Vamos a crear un nuevo proyecto ionic 2 que en este caso le vamos a llamar adivina: abrimos el terminal o consola de comandos y escribimos:

ionic start adivina blank

Nos situamos dentro de la carpeta del proyecto de acabamos de crear:

cd adivina

Y una vez dentro ejecutamos ionic serve para ver en el navegador los cambios que vayamos haciendo en nuestra aplicación.

ionic serve

Recuerda que si utilizas la opción -l con ionic serve puede visualizar como queda la app en todas las plataformas.

Ahora abrimos la carpeta del proyecto que acabamos de crear en el editor, entramos en src/pages/home y abrimos el archivo home.html.

Editamos su contenido para que quede de la siguiente manera:

<ion-header>
  <ion-navbar>
    <ion-title>
      Adivina el n√ļmero secreto
    </ion-title>
  </ion-navbar>
</ion-header>
<ion-content padding>
    <ion-input type="number" min="1" max="100" [(ngModel)]="num" placeholder="Introduce un n√ļmero del 1 al 100"></ion-input>
    <p>El n√ļmero secreto es {{ mayorMenor }}</p>
   <button ion-button block (click)="compruebaNumero()">Adivina</button>
</ion-content>

Si nunca antes has programado en angular ni en ionic es posible que el c√≥digo te resulte extra√Īo, no te preocupes, ahora vamos a ver lo que es cada cosa, si por el contrario ya has programado en angular o ionic 1 es probable este c√≥digo te resulte familiar.

Tenemos una plantilla b√°sica de una p√°gina con ion-header, ion-navbar y ion-title donde hemos puesto el t√≠tulo de nuestra aplicaci√≥n “Adivina el n√ļmero secreto”. Hasta aqu√≠ ninguna novedad con lo que ya hab√≠amos visto en el cap√≠tulo anterior.

Dentro de ion-content que como sabemos es donde va el contenido de la p√°gina es donde est√° lo interesante:

 <ion-input type="number" min="1" max="100" [(ngModel)]="num" placeholder="Introduce un n√ļmero del 1 al 100"></ion-input>

Primero tenemos un ion-input que es muy parecido a un input de html, le decimos que sea de tipo number, le ponemos un rango mínimo a 1 y máximo a 100.

El siguiente par√°metro [(ngModel)]¬†es el encargado de hacer el Data Binding entre el valor del input y una variable llamada num que estar√° definida como luego veremos en el controlador de la p√°gina. Este es un Data Binding bidireccional ya que si se introduce un valor en el input autom√°ticamente este ser√° reflejado en la variable del controlador, del mismo modo si alg√ļn proceso cambia el valor de esta variable en el controlador este autom√°ticamente aparecer√° reflejado como valor del input. Por √ļltimo el input tiene un placeholder indicando que se introduzca un valor entre 1 y 100.

La siguiente línea es un párrafo que contiene lo siguiente:

<p>El n√ļmero secreto es {{ mayorMenor }}</p>

Si ya conoces ionic o angular sabes de que se trata. En este caso las con las dobles llaves “{{ }}” ¬†hacemos un Data Binding unidireccional, mayorMenor es una variable que estar√° definida en el controlador y con las dobles llaves muestra su valor. En este caso la variable contendr√° el texto “mayor” en caso de que el n√ļmero secreto sea mayor al introducido, “menor” en caso de ser menor e “igual” en caso de ser igual.

Por √ļltimo tenemos un bot√≥n para comprobar si el n√ļmero introducido coincide con el n√ļmero secreto.

<button ion-button block (click)="compruebaNumero()">Adivina</button>

En la documentación oficial de ionic podéis ver más sobre como crear distintos tipos de botones con ionic 2: https://ionicframework.com/docs/v2/components/#buttons

De momento esto es todo lo que necesitamos en la plantilla de nuestro juego.

Ahora necesitamos programar el comportamiento en el controlador de la p√°gina.

Si abrimos el archivo home.ts que se genera automáticamente al crear un proyecto en blanco veremos el siguiente código:

import { Component } from '@angular/core';

import { NavController } from 'ionic-angular';

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

  constructor(public navCtrl: NavController) {

  }

}

Este c√≥digo esta es typeScript. Como ya hemos comentado TypeScript permite utilizar elementos de las √ļltimas versiones del est√°ndar ECMAscript aunque todav√≠a no est√©n implementadas en el navegador que estemos utilizando. Tambi√©n nos permite utilizar variables tipadas.

Ionic 2 se basa Angular 2 que a su ver utiliza TypeScript, así que es muy beneficioso conocer angular 2 y typescript.
Aquí vamos a explicar a groso modo las cuestiones imprescindibles para poder desenvolvernos con ionic 2 pero para saber más sobre angular y sacarle el máximo partido podéis consultar en http://learnangular2.com/  así como otros muchos recursos que se pueden encontrar en la red para aprender Angular 2.

Analicemos el código de home.ts:

Lo primero que vemos son dos lineas import:

import { Component } from '@angular/core';

import { NavController } from 'ionic-angular';

Import se utiliza para importar módulos que contienen librerías y clases para poder utilizarlas en nuestro proyecto. Podemos importar módulos propios de Ionic que ya se incorporan al crear un proyecto, librerías de AngularJS, librerías de terceros que podemos instalar o nuestras propias  librerías.

En este caso se importa el elemento Component de Angular y el elemento NavController de ionic-angular.

NavController lo utilizaremos m√°s adelante para poder navegar entre distintas p√°ginas, de momento en este ejemplo no lo necesitamos.

Las p√°ginas son componentes de Angular, por eso necesitamos importar Component.

Después vemos el siguiente código:

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

@Component es un decorador de Angular. Angular 2 usa los decoradores para registrar un componente.

Los decoradores empiezan con @.

Los decoradores se colocan antes de la clase y definen cómo se va a comportar esta.

Existen los siguientes  decoradores:

  • @Component
  • @Injectable
  • @Pipe
  • @Directive

De momento nos interesa @Component.

En el c√≥digo vemos que @Component contiene un objeto con dos atributos, selector:’page-home’, que es el selector css que se va a aplicar a la p√°gina, y templateUrl:’home.html’¬†que es la plantilla html que va a renderizar la p√°gina.

Por √ļltimo se exporta una clase llamada HomePage:

export class HomePage {

  constructor(public navCtrl: NavController) {

  }

}

Tenemos que exportar la clase para luego poderla importar si queremos llamar a  la página desde cualquier otro sitio de la aplicación, por ejemplo cuando navegamos entre páginas.
Vemos que la clase tiene un constructor, en TypeScript la creación de clases es muy similar a como sería en otros lenguajes de programación orientado a objetos como Java.

Todos los datos que se muestran en la plantilla y todas las funciones a las que se llame por ejemplo al hacer click en un botón deben formar parte de la clase HomePage qué es el controlador de la página.

Como ya hemos visto en la plantilla home.html utilizamos¬†(ngModel)]=”num”¬†para recoger en la variable num el valor que se introduzca en el campo input, tambi√©n mostr√°bamos otra variable llamada¬†mayorMenor que indicar√° ¬†si el n√ļmero introducido es mayor o menor que el n√ļmero que hay que adivinar:

 <ion-input type="number" min="1" max="100"[(ngModel)]="num" placeholder="Introduce un n√ļmero del 1 al 100"></ion-input>
    <p>El n√ļmero secreto es {{ mayorMenor }}</p>

Por lo tanto estas variables deben de estar definidas en el controlador dentro de la clase HomePage del archivo home.ts:

export class HomePage {

  num:number;
  mayorMenor: string = '...';

  constructor(public navCtrl: NavController) {

  }

}

Como podemos observar para definir una variable ponemos el nombre de la variable y seguido de dos puntos “:”¬†el tipo de valor que va a contener. Si lo deseamos podemos inicializar la variable con un valor en el momento de declararla. En este caso num es de tipo number y mayorMenor de tipo string ya que va a contener un texto con la palabra “Mayor”, “Menor” o “Igual” seg√ļn sea el caso. Inicializamos la variable mayorMenor con tres puntos suspensivos “…”.

Los tipos primitivos  de variable que podemos definir son:

  • number (Num√©rico).
  • string (cadenas de texto).
  • boolean (Booleano: true o false).
  • any (cualquier tipo).
  • Array.

Tambi√©n vamos a necesitar otra variable que contenga el n√ļmero secreto que debemos adivinar, le vamos a llamar numSecret y como valor le vamos a asignar la respuesta a la llamada a una funci√≥n llamada numAleatorio que crearemos a continuaci√≥n.

Definimos la variable numSecret de tipo number:

numSecret: number = this.numAleatorio(0,100);

Hacemos referencia a la  función numAleatorio con this porque va a ser un método de la propia clase.

Ahora vamos a ¬†crear la funci√≥n numAleatorio que nos devolver√° un n√ļmero aleatorio.

numAleatorio(a,b)
{
      return Math.round(Math.random()*(b-a)+parseInt(a));
}

Esta funci√≥n recibe como par√°metros dos valores que ser√°n el rango m√≠nimo y el m√°ximo para el n√ļmero aleatorio, es este caso le estamos pasando 0 y 100 por lo que obtendremos un n√ļmero aleatorio entre 0 y 100.

Por √ļltimo vamos a crear la funci√≥n compruebaNumero que se llama al pulsar el bot√≥n Adivina¬†que en la parte html lo hemos definido as√≠:

<button ion-button block (click)="compruebaNumero()">Adivina</button>

Y ahora en home.ts definimos la función dentro de la clase HomePage:

compruebaNumero(){
    if(this.num)
    {
      if(this.numSecret < this.num)
      {
        this.mayorMenor = 'menor';
      }
      else if(this.numSecret > this.num)
      {
        this.mayorMenor = 'mayor';
      }
      else{
        this.mayorMenor = 'igual';
      }
    }
  }

Esta funci√≥n compara¬†el n√ļmero secreto que esta contenido en la variable numSecret con¬†el n√ļmero introducido por el usuario que se aloja en la variable num y le asigna a la variable mayorMenor el texto “menor”, “mayor” o “igual” en funci√≥n de si es menor, mayor o igual que esta.
Observa que debemos utilizar this para las variables ya que son atributos de la propia clase.

El código completo de home.ts quedará de esta manera:

import { Component } from '@angular/core';

import { NavController } from 'ionic-angular';

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

  num:number;
  mayorMenor: string = '...';
  numSecret: number = this.numAleatorio(0,100);

  constructor(public navCtrl: NavController) {

  }

  compruebaNumero(){
    if(this.num)
    {
      if(this.numSecret < this.num)
      {
        this.mayorMenor = 'menor';
      }
      else if(this.numSecret > this.num)
      {
        this.mayorMenor = 'mayor';
      }
      else{
        this.mayorMenor = 'igual';
      }
    }
  }

  numAleatorio(a,b)
  {
        return Math.round(Math.random()*(b-a)+parseInt(a));
  }

}

Ahora si lo ejecutamos en el navegador con ionic serve¬†(si no lo ten√≠as ya en marcha), podemos ver un campo para introducir un n√ļmero, y debajo un texto que dice “el n√ļmero secreto es … de”, en estos momentos ¬†no hay introducido ning√ļn n√ļmero por lo que no aparece ning√ļn n√ļmero en el texto.

Si introducimos un n√ļmero vemos que autom√°ticamente en el texto aparece ese n√ļmero el mismo tiempo que lo escribimos. Eso es porque estamos realizando un Data Binding con la variable num. Al utilizar [(ngModel)]=”num” en el campo input le estamos indicando que la variable coja el valor que introducimos en el input y lo refresque autom√°ticamente en todos los sitios donde se utilice, por ejemplo en el texto de debajo.Lo mismo ocurre si cambiamos el valor de la variable desde el c√≥digo.

Por √ļltimos si pulsamos el bot√≥n adivina obtendremos algo como esto:

Vista de nuestro Juego en acción
Vista de nuestro Juego en acción

Al pulsar el bot√≥n se ha ejecutado la funci√≥n¬†compruebaNumero(), dentro de la funci√≥n se ha comprobado que el n√ļmero secreto es mayor a 12 por lo que a la variable mayorMenor se le asigna el texto “mayor”.

Cuando acertemos el n√ļmero secreto el texto dir√° que el n√ļmero secreto es igual al n√ļmero introducido. Esto es un poco soso, vamos a hacer algunos cambios para que cuando acertemos el n√ļmero nos muestre un mensaje felicit√°ndonos por haber acertado.

Este mensaje lo ponemos en la plantilla html pero solo se debe mostrar cuando se cumpla una condici√≥n y es que la variable mayorMenor¬†contenga el texto ‘igual’.

Vamos a a√Īadir lo siguiente en home.html justo antes del bot√≥n “Adivina”:

<ion-card *ngIf="mayorMenor=='igual'">
     <ion-card-header>
        ¬°¬°¬°Enhorabuena!!!
      </ion-card-header>
      <ion-card-content>
          Has acertado, el n√ļmero secreto es el {{ num }}
      </ion-card-content>
</ion-card>

Aprovecho la ocasi√≥n para introducir otro componente de ionic: ion-card, las cards o “tarjetas” son componentes que muestran la informaci√≥n en un recuadro. Como vemos dentro contiene otros dos componentes <ion-card-header> y <ion-card-content>,¬†Como habr√°s adivinado el primero permite mostrar una cabecera dentro de la tarjeta y el segundo el contenido que deseemos.
En la documentación oficial de ionic podéis ver todas las posibilidades que tiene ion-card: https://ionicframework.com/docs/v2/components/#cards

Queremos que est√° card se muestre solo cuando la variable de nuestro controlador mayorMenor¬†contenga el texto ‘igual’ despu√©s de hacer la comprobaci√≥n.

Aquí entra en juego la directiva condicional *ngIf:

<ion-card *ngIf="mayorMenor=='igual'">

La directiva *ngIf es una directiva estructural, lo que significa que nos permite alterar el DOM (Document Object Model), estas directivas llevan un asterisco “*” delante.
Con *ngIf le estamos indicando que el elemento donde se encuentra, ion-card en este caso, solo se muestre si se cumple la condici√≥n que tiene definida entre las dobles comillas. En este caso le estamos diciendo que solo se muestre el elemento ion-card cuando mayorMenor==’igual’.

Sabiendo esto vamos a darle otra vuelta de tuerca m√°s y vamos a a√Īadir un bot√≥n para volver a jugar una vez que hemos acertado, este bot√≥n llamar√° a una funci√≥n para volver a generar un nuevo n√ļmero aleatorio y reiniciar√° las variables para que se oculte el ion-card. Este bot√≥n debe permanecer oculto hasta que hayamos acertado, y cuando se muestra debe ocultarse el bot√≥n Adivina para que no podamos pulsarlo despu√©s de haber adivinado el n√ļmero hasta que empecemos una nueva partida.

Editamos home.html y a√Īadimos *ngIf al bot√≥n Adivina para que solo se ¬†muestre cuando no hemos acertado el n√ļmero:

<button *ngIf="mayorMenor!='igual'" ion-button block (click)="compruebaNumero()">Adivina</button>

Ahora a√Īadimos el bot√≥n “Volver a Jugar” a home.html:

<button *ngIf="mayorMenor=='igual'" ion-button block (click)="reinicia()">Volver a Jugar</button>

Con *ngIf indicamos que solo se muestre cuando hayamos acertado el n√ļmero, y en el evento (click) le indicamos que ejecute la funci√≥n reinicia().

Por lo tanto ahora ¬†vamos a editar nuestro controlador (home.ts) y a√Īadimos la funci√≥n reinicia(), que como hemos dicho debe reiniciar las variables para poder comenzar el juego:

reinicia(){
    // reiniciamos las variables
    this.num = null;
    this.mayorMenor = '...';
    this.numSecret = this.numAleatorio(0,100);
  }

Ahora ya podemos volver a jugar una vez de que hemos adivinado el n√ļmero.

Este juego se podr√≠a mejorar a√Īadiendo un contador de intentos. Podr√≠amos limitar el n√ļmero de intentos y mostrar un mensaje si se han consumido todos los intentos sin acertar el n√ļmero secreto. Esto lo dejo como deberes para el que quiera practicar m√°s, si quer√©is ver como ser√≠a la soluci√≥n completa con contador de intentos¬†Luis Jord√°n¬†me ha hecho llegar su soluci√≥n, en el siguiente enlace pod√©is encontrar el c√≥digo de la p√°gina home que ha mejorado realizado Luis:¬†https://drive.google.com/file/d/0B7M15FxlLmQJZHBSTUlfMmFkSmM/view?usp=sharing

De momento este simple ejemplo nos ha servido para aprender algunos conceptos básicos sobre Ionic 2, en el siguiente capitulo veremos como navegar entre distintas páginas de una aplicación.

 

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

Tutorial de Ionic – Hola Mundo en Ionic

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 capitulo anterior vimos como instalar ionic y las herramientas de desarrollo que necesitamos para crear aplicaciones multiplataforma con ionic 2.

Ahora vamos a crear nuestra primera aplicaci√≥n con Ionic 2, el famoso “hola mundo” que siempre es el punto inicial en el aprendizaje de cualquier lenguaje de programaci√≥n o framework.

Para crear¬†nuestro proyecto “hola mundo” iremos a la consola de comandos o terminal y escribimos el siguiente comando:

ionic start hola-mundo blank

Tras un rato descargando los paquetes necesarios para crear nuestra aplicación ya estará listo nuestro proyecto.

El comando start de ionic cli (commandline interface)  se utiliza  para crear (iniciar) un nuevo proyecto, el siguiente parámetro es el nombre del proyecto, en este caso hola-mundo, el siguiente parámetro es el tipo de plantilla que vamos a utilizar, en este caso blank que indica que utilizaremos una plantilla vacía.

Existen tres tipos de plantillas:

  • blank : Crea una plantilla vac√≠a.
  • sidemenu: Crea una plantilla con men√ļ lateral.
  • tabs: Crea una plantilla con Tabs (Pesta√Īas).¬†

Para ver el resultado en el navegador debemos entrar dentro de la carpeta del proyecto ‘hola-mundo’ que se acabamos de crear con ionic start y escribimos el siguiente comando:

ionic serve -l

Con el parámetro -l nos muestra como queda nuestra app en las tres plataformas soportadas, es decir, IOS, Android  y Windows.

captura-de-pantalla-2016-12-13-a-las-19-21-19
Vista de como quedaría nuestra primera App con plantilla Blank en el navegador.

Si no utilizamos -l mostrar√° solo una plataforma.

Para ver las posibilidades de ionic podemos crear un proyecto de prueba con plantilla tabs  y otro con sidemenu:

ionic start appTabs tabs
Aplicación con plantilla tabs
Aplicación con plantilla tabs
ionic start appSide sidemenu
Aplicación con plantilla sidemenu
Aplicación con plantilla sidemenu

Como podemos ver sin nosotros hacer nada tenemos creada la estructura de una app que podremos modificar para a√Īadir lo que necesitemos.

En el próximo capitulo profundizaremos en la estructura de carpetas de una aplicación con ionic 2 y modificaremos nuestro hola mundo.

 

Tutorial de Ionic РConstruye Apps móviles multiplataforma con ionic desde cero

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

 

¬°¬°Atenci√≥n!! este tutorial se basa en ionic 2/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 voy a comenzar con la serie de entradas destinadas a aprender a crear aplicaciones móviles multiplataforma utilizando Ionic.

Introducción

Aunque este tutorial est√° destinado a aprender ionic desde cero si que es recomendable tener un conocimiento b√°sico de javascript, html y css, por lo que si eres desarrollador web podr√°s reciclar tus conocimientos.

Aunque los puristas tal vez piensen que es mejor aprender una base teórica solida antes de empezar a programar nada, creo que esto puede hacer que muchos desistan  por el camino. En cambio es mucho más motivador ver que nada más empezar se puede hacer cosas que funcionan aunque sean sencillas y ir sobre la marcha aprendiendo conceptos a medida que los vamos necesitando.

Es probable que se queden cosas en el tintero pero aprenderás lo suficiente para empezar a desarrollar  tus propias apps y podrás investigar por tu cuenta cuando te encuentres con alguna necesidad que no este explicada en este tutorial.

Iremos aprendiendo las cosas sobre la marcha seg√ļn las vayamos necesitando en los ejemplos de aplicaciones que vamos a realizar.

Voy a intentar separar el tutorial en entradas relativamente cortas para que se haga m√°s ameno el aprendizaje.

Vamos a ver una peque√Īa introducci√≥n sobre que es ionic y que nos aporta en el desarrollo de aplicaciones m√≥viles.

¬ŅQues es ionic?

Ionic es un framework que nos permite crear de una manera rápida y sencilla aplicaciones móviles multiplataforma (Android, IOS, Windows) utilizando tecnologías web (HTML, JAVASCRIPT, CSS).
Para poder utilizar elementos web en la app utiliza lo que se conoce como una  Webview.
A este tipo de aplicaciones se las conoce como aplicaciones h√≠bridas.¬†El resultado final es una app “nativa”¬†que puedes subir a las tiendas de apps.

Ionic 2 esta basado en Apache Cordova y Angular 2, por lo que serán necesarios unos conocimientos básicos de estas tecnologías para sacar mayor provecho al desarrollo.

Adem√°s ionic 2 utiliza TypeScript como lenguaje de programaci√≥n, si no dominas las anteriores tecnolog√≠as mencionadas no te preocupes, tratare de ir explicando las cosas b√°sicas necesarias seg√ļn las vayamos necesitando en los ejemplos que realizaremos.

No profundizare en cada tecnolog√≠a, es decir no voy a hacer un tutorial ¬†completa de Angular 2, seguro que puedes encontrar con facilidad muchos en la web, solo aprenderemos una peque√Īa base que nos permita saber lo suficiente para realizar las cosas m√°s comunes de una app, sin embargo podr√°s investigar por tu cuenta si en alg√ļn momento necesitas saber algo m√°s sobre Angular.

Al terminar sabr√°s lo suficiente para defenderte y crear tus propias aplicaciones.

Ventajas de utilizar ionic para desarrollar apps

La principal ventaja de utilizar Ionic es que es multiplataforma, es decir que con un mismo código podemos generar apps para Android, IOS y Windows, por lo que tiempo y coste de desarrollo y mantenimiento de una app se reduce sensiblemente.

Otra ventaja es que si dispones de conocimientos previos en desarrollo web frontend ya tienes medio camino andado ya que la curva de aprendizaje ser√° mucho menor.

Adem√°s Ionic dispone de muchos componentes ya creados para que sin apenas esfuerzos puedas desarrollar una app de apariencia profesional¬†sin necesidad de ser un gran dise√Īador.

Desventajas de utilizar ionic para desarrollar apps

La principal diferencia con las apps puramente nativas es que estas utilizan los elementos de la interfaz nativa de la plataforma en lugar de correr en una webview, lo que supone una mayor fluidez en el funcionamiento de la app a la hora de cambiar de pantalla, hacer scroll, etc, sin embargo con los dispositivos cada vez m√°s potentes que existen en el mercado y la mejora en el rendimiento de las webview que incorporan las versiones modernas de los sistemas operativos m√≥viles, ¬†esta diferencia en el rendimiento es cada vez menos notoria y en la mayor√≠a de los casos la experiencia de usuario de una aplicaci√≥n h√≠brida desarrollada con ionic bien dise√Īada¬†ser√° muy similar a la de una aplicaci√≥n nativa.

Diferencia entre Ionic 2 e Ionic 1

En ionic 1 la apariencia de la App era igual para cada plataforma salvo que modificases elementos en funci√≥n de la¬†plataforma lo que hace m√°s engorroso¬†el dise√Īo si queremos diferenciar el dise√Īo¬†seg√ļn la¬†plataforma.
Con¬†ionic 2 sin tener que modificar nada tendremos un dise√Īo con el estilo propio de cada plataforma (con material design en caso de Android) dando una ¬†apariencia de app nativa.

La estructura del proyecto y la organización del código esta mejor estructurada y es más modular, lo que nos permite un desarrollo mas organizado y fácil de mantener.

Ionic 2 dispone del comando Ionic generator que nos permite desde consola crear diferentes elementos como p√°ginas, tabs, providers etc, ahorrandonos tiempo de desarrollo. Veremos Ionic generator y m√°s adelante.

Ionic 2 se basa en Angular 2 por lo que incorpora las mejoras en cuanto a rendimiento que ha incorporado AngularJs en su nueva versión.

Como ya he mencionado Ionic 2 utiliza typescript, lo que nos permite utilizar toda la potencia de la programación orientada a objetos, tipado estático, además nos permite utilizar todos los elementos de EcmaScript 6 y muchos del futuro EcmacScript 7. Veremos esto con más profundidad en próximos capítulos.