Archivo de la etiqueta: Android

Como crear componentes personalizados con Ionic

 

¡¡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 posts anteriores hemos aprendido a crear nuestras apps multiplataforma con Ionic.
Ionic nos ofrece un montón de componentes ya creados para utilizar en nuestras apps y realmente nos permiten con poco esfuerzo crear una interfaz funcional para nuestras aplicaciones.

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

Sin embargo hay momentos que puede interesarnos crear nuestros propios componentes personalizados.

Un componente es simplemente algo que podemos mostrar las veces que queramos en la pantalla, como si fuese una etiqueta html, solo que a su vez un componente puede estar formado por etiquetas html y otros componentes.

Para ver mejor como podemos crear nuestro propios componentes personalizados en Ionic vamos a crear un proyecto de prueba al que vamos a llamar miComponente:

ionic start miComponente blank

Una vez creado el proyecto si  entramos en home.ts veremos esto:

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

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

  constructor(public navCtrl: NavController) {

  }

}

En Ionic todo son componentes, de hecho las páginas de nuestra aplicación son componentes, si nos fijamos tiene un decorador @Component por lo que podemos ver que la propia pagina es un componente  que tiene el selector ‘page-home’ y como plantilla utiliza el archivo home.html.

Vamos a crear un sencillo componente al que vamos a llamar saludar, para ello vamos a echar mano de ionic generator, nos situamos dentro la carpeta de  nuestro proyecto en la consola de comandos y tecleamos lo siguiente:

ionic g component saluda

Con esto se habrá creado una nueva carpeta llamada components y dentro una carpeta llamada saluda que al igual que las páginas contiene un archivo .ts como controlador, un archivo .scss para los estilos y su plantilla .html.

Si observamos lo que contiene el archivo saluda.html vemos que simplemente muestra el contenido de la variable text dentro de un div:

<!-- Generated template for the SaludaComponent component -->
<div>
  {{text}}
</div>

Vemos que text está definida en el controlador saluda.ts y contiene la famosa frase “Hello Word”

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

/**
 * Generated class for the SaludaComponent component.
 *
 * See https://angular.io/api/core/Component for more info on Angular
 * Components.
 */
@Component({
  selector: 'saluda',
  templateUrl: 'saluda.html'
})
export class SaludaComponent {

  text: string;

  constructor() {
    console.log('Hello SaludaComponent Component');
    this.text = 'Hello World';
  }

}

Como vemos el controlador de un componente es prácticamente igual que el de una página.

Tenemos el decorador @Component donde se indica que su selector es ‘saluda‘ y la plantilla que utiliza es saluda.html.

Para poderlo utilizar tenemos que importar y declarar el componente en app.module.ts:

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

import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { SaludaComponent } from '../components/saluda/saluda';


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

Para utilizarlo en la página home editamos el archivo home.html y añadimos la etiqueta con el componente que acabamos de crear:

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  <saluda></saluda>
</ion-content>

Así de fácil.

Si ejecutamos nuestra app de ejemplo con ionic serve -l veremos algo similar a esto:

 

A nuestro componente le podemos añadir también atributos personalizados.
Por ejemplo podemos pasarle un atributo que se llame nombre de esta manera:

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
 <saluda nombre="Eduardo"></saluda>
</ion-content>

Luego en el controlador definimos el parámetro de entrada con el decorador Input de la siguiente manera:

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

/**
 * Generated class for the SaludaComponent component.
 *
 * See https://angular.io/api/core/Component for more info on Angular
 * Components.
 */
@Component({
  selector: 'saluda',
  templateUrl: 'saluda.html'
})
export class SaludaComponent {

  text: string;
  @Input('nombre') nombre : string;

  constructor() {
    console.log('Hello SaludaComponent Component');
    this.text = 'Hello World';
  }

}

Para poder utilizar el decorador Input debemos importarlo primero.

Ahora podemos hacer que en lugar de saludar al mundo con “Hello Word” salude a la persona que recibamos en el parámetro nombre:

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

/**
 * Generated class for the SaludaComponent component.
 *
 * See https://angular.io/api/core/Component for more info on Angular
 * Components.
 */
@Component({
  selector: 'saluda',
  templateUrl: 'saluda.html'
})
export class SaludaComponent {

  text: string;
  @Input('nombre') nombre : string;

  constructor() {
    console.log('Hello SaludaComponent Component');
    this.text = 'Hola '+this.nombre;
  }

}

Bien, si como en el ejemplo hemos pasado “Eduardo” al parámetro nombre cabría esperar ver en pantalla “Hola Eduardo”, sin embargo comprobamos que muestra “Hola undefined”, a no ser que tus padres te hayan puesto de nombre undefined hay algo que no está funcionando.

Eso eso es porque estamos accediendo a la variable this.nombre desde el constructor, y en el momento que se ejecuta el constructor aun no están accesibles los parámetros que recibimos en nuestro componente, para ello utilizamos ngOnInit:

/**
 * Generated class for the SaludaComponent component.
 *
 * See https://angular.io/api/core/Component for more info on Angular
 * Components.
 */
@Component({
  selector: 'saluda',
  templateUrl: 'saluda.html'
})
export class SaludaComponent {

  text: string;
  @Input('nombre') nombre : string;

  constructor() {
    console.log('Hello SaludaComponent Component');
  }


  ngOnInit(){
    this.text = 'Hola '+this.nombre;
  }


}

Ahora podemos comprobar que muestra el nombre que le hayamos pasado.

También podemos utilizar la la variable nombre directamente en la plantilla de nuestro componente de esta manera:

<!-- Generated template for the SaludaComponent component -->
<div>
  Hola {{ nombre }}
</div>

Por ultimo si en lugar de pasarle directamente el nombre al componente queremos utilizar una variable debemos poner el parámetro entre corchetes , por ejemplo imaginemos que tenemos un array de usuarios  y queremos saludarles a todos, en home.ts definiriamos un array de usuarios:

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

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

  usuarios: any = [
    { 
      nombre: 'Eduardo',
      edad: 41
    },
    { 
      nombre: 'Pedro',
      edad: 28
    },
    { 
      nombre: 'Francisco',
      edad: 34
    },
    { 
      nombre: 'Maria',
      edad: 43
    }
  ]

  constructor(public navCtrl: NavController) {

  }

}

Ahora en home.html podemos recorrer el array con *ngFor y mostrar nuestro componente saludo pasándole la variable usuario.nombre:

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
 <saluda  *ngFor="let usuario of usuarios" [nombre]="usuario.nombre"></saluda>
</ion-content>

Como podemos ver en este caso el parámetro nombre va entre corchetes []  ya que lo que le pasamos no es un texto literal sino una variable.

Si probamos este ejemplo veremos algo como esto:

Este ejemplo es muy sencillo y es solo para explicar como funcionan los componentes en Ionic, evidentemente no merece la pena crear un componente que solo contenga un div y un texto, pero podemos ampliar nuestro componente añadiendo una imagen o una ficha completa con los datos del usuario, o cualquier otro elemento que se nos ocurra.

Espero que estos sencillos ejemplos sirvan para comprender como crear componentes personalizados en Ionic.

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

Como crear una app multi idioma con Ionic

 

¡¡Atención!! este tutorial se basa en ionic 3 y está desactualizado por lo que es posible que los ejemplos no funcionen en la última versión de ionic, haz click aquí para acceder a un tutorial mas actual de Ionic.

Hola a todos, hoy vamos a aprender como crear una app multi idioma con Ionic.

Como siempre vamos a crear una app de ejemplo así que desde consola creamos un nuevo proyecto al que vamos a llamar ejemploidioma:

ionic start ejemploidioma blank --cordova

Con –cordova le indicamos que incluya apache cordova para poder generar el código para android e ios.

Para crear un app multi idioma con Ionic vamos a instalar la librería  ngx-translate:

npm install npm install @ngx-translate/core @ngx-translate/http-loader --save

Ahora en app.module.ts tenemos que añadir los siguientes imports:

import { TranslateModule, TranslateLoader } from "@ngx-translate/core";
import { TranslateHttpLoader } from "@ngx-translate/http-loader";
import { HttpClient, HttpClientModule } from "@angular/common/http";

Justo debajo de los imports, antes de @NgModule añadimos el siguiente código:

export function HttpLoaderFactory(httpClient: HttpClient) {
  return new TranslateHttpLoader(httpClient, "../assets/i18n/", ".json");
}

Por último declaramos dentro de imports lo que esta marcado en amarillo:

@NgModule({
  declarations: [
    MyApp,
    HomePage
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
    HttpClientModule,
    TranslateModule.forRoot({
          loader: {
              provide: TranslateLoader,
              useFactory: HttpLoaderFactory,
              deps: [HttpClient]
          }
      })
 
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ]
})

El código completo de app.module.ts quedaría de la siguiente manera:

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

import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';

import { TranslateModule, TranslateLoader } from "@ngx-translate/core";
import { TranslateHttpLoader } from "@ngx-translate/http-loader";
import { HttpClient, HttpClientModule } from "@angular/common/http";

export function HttpLoaderFactory(httpClient: HttpClient) {
  return new TranslateHttpLoader(httpClient, "../assets/i18n/", ".json");
}

@NgModule({
  declarations: [
    MyApp,
    HomePage
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
    HttpClientModule,
    TranslateModule.forRoot({
          loader: {
              provide: TranslateLoader,
              useFactory: HttpLoaderFactory,
              deps: [HttpClient]
          }
      })
 
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ]
})
export class AppModule {}

Ahora vamos a editar app.component.ts  y añadimos el siguiente código:

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 { TranslateService } from '@ngx-translate/core';

import { HomePage } from '../pages/home/home';

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

  constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen, private translateService: TranslateService) {
    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.
      this.translateService.setDefaultLang('es');
      this.translateService.use('es');

      statusBar.styleDefault();
      splashScreen.hide();
    });
  }
}

Con this.translateService.setDefaultLang(‘es’); le idicamos que el idioma por defecto es el español y con this.translateService.use(‘es’); hacemos que sea el idioma seleccionado.

Dentro de la carpeta src/assets vamos a crear una carpeta llamada i18n y dentro de esta situaremos los archivos con los literales en los diferentes idiomas, en este ejemplo vamos a usar inglés y español, por lo que vamos a crear dos archivos llamados en.json para inglés y es.json para español:

En en.json pondremos  lo siguiente:

{
  "HOLA": "Hi {{ value }}",
  "IDIOMA": "Language",
  "BIENVENIDO" : "Welcome"
}

 

Y en es.json:

{
  "HOLA": "Hola {{ value }}",
  "IDIOMA": "Idioma",
  "BIENVENIDO" : "Bienvenido"
}

 

Como puedes observar es un archivo en formato json donde la clave es lo que escribiremos en la vista y  el valor es lo que se mostrará en función del idioma seleccionado.

Como puedes ver HOLA se traduce como “Hola {{ value }}”, donde value se sustituirá por el valor pasado, veremos un poco más adelante como se utiliza.

Ya estamos preparados para traducir  nuestra app.

Vamos a modificar home.html y vamos a crear un ejemplo sencillo donde vamos a tener un elemento ion-card que simplemente nos da la Bienvenida y nos saluda y un selector para cambiar de idioma:

<ion-header>
  <ion-navbar>
    <ion-title>
      Ejemplo Idioma
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  <ion-card>
    <ion-card-header text-center>
      {{ 'BIENVENIDO' | translate }}
    </ion-card-header>
      
    <ion-card-content>
      <p text-center>
        {{ 'HOLA' | translate:{value: 'Eduardo'} }} 
      </p>
    </ion-card-content>
  </ion-card>

  <ion-item>
    <ion-label>{{ 'IDIOMA' | translate }} </ion-label>
    <ion-select [(ngModel)]="idioma" (ionChange)="cambioIdioma($event)">
      <ion-option value="es">Español</ion-option>
      <ion-option value="en">Inglés</ion-option>
    </ion-select>
  </ion-item>
</ion-content>

Como vemos en la etiqueta ion-select le decimos que en el evento (ionChange) llame a la función cambioIdioma($event), $event contendrá la opción seleccionada.

Para traducir las cadenas al idioma seleccionado utilizamos en pipe o barra vertical ‘|’ seguido de la palabra translate que es filtro que se aplica.

Como comenté antes podemos pasarle un valor al filtro translate de la siguiente manera:

{{ ‘HOLA’ | translate:{value: ‘Eduardo’} }}

Con esto le estamos diciendo que traduzca la cadena HOLA y le pase al fintro el value ‘Eduardo’.

Al ir a buscar el valor en el archivo json sustituirá valúe por ‘Eduardo’:

“HOLA”: “Hi {{ value }}”,

Esto puede servirnos de ayuda cuando queremos traducir cadenas mostrando un valor que dependiendo del idioma se debe mostrar en una posición diferente de la cadena, por ejemplo la frase “Este es el coche de Eduardo” en inglés sería “this is Eduardo’s car”.

Por lo tanto en inglés podríamos pasarle el nombre y traducirlo así:
“this is {{ value }}’s car”,

Mientras que en español sería
“Este es el coche de {{ value }}”

Bien, ahora solo nos queda definir la función cambioIdioma en el controlador home.ts, para poder cambiar de idioma necesitaremos importar TranslateService e inyectarlo en el constructor:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';

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

  constructor(public navCtrl: NavController, private translateService: TranslateService) {

  }

  cambioIdioma(selectedValue){
    this.translateService.use(selectedValue);
  }

}

Con selectedValue es el idioma que hemos seleccionado en el select y con this.translateService.use(selectedValue) le estamos diciendo que utilice el idioma que le pasamos.

Ya podemos probar nuestro pequeño ejemplo, ejecutando ionic serve -l desde consola veremos algo como esto en nuestro navegador:

Ejemplo de app multi idioma con ionic
Ejemplo de app multi idioma con ionic

Por último, como extra si desde el controlador necesitamos traducir una cadena podemos utilizar this.translateService.instant().

Por ejemplo si queremos mostrar un loading y que se muestra cargando en el idioma seleccionado, teniendo ya definido el literal cargando en los ficheros .json podemos hacer lo siguiente:
this.loading = this.loadingCtrl.create({
      content: this.translateService.instant('Cargando'),
      spinner: 'dots',
    });
Hay que tener e cuenta que  translate.instant es síncrono, por lo que tenemos que estar seguros de que  los archivos lang  han sido cargados para obtener traducciones.
En su lugar también se puede utilizar translate.get, que devuelve un elemento observable.
Para saber más sobre ngx-translate podeis visitar su página oficial en http://www.ngx-translate.com/
Eso es todo por hoy, espero que te sea de utilidad.

 

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

Directiva para mostrar y ocultar menú al hacer scroll en Ionic

 

¡¡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.

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

Hola a todos:

El post de hoy es por cortesía de Miguel Escribano.

Miguel ha realizado una app en ionic para consultar las noticias de menéame que está realmente bien y que podéis encontrar en google play en el siguiente enlace:

https://play.google.com/store/apps/details?id=com.miguelescribano.meneame

Bién, Miguel  que ha querido colaborar aportando una directiva para mostrar u ocultar un menú o barra de herramientas al hacer scroll:

Una directiva es un tipo de componente pensado para cambiar el comportamiento de un elemento ya existente en el DOM.  A diferencia de un componente normal este no necesita de una plantilla sino que se incrustara en una existente para manipularla.

El objetivo aquí es esconder una barra de navegación al hacer scroll hacia abajo y que aparezca cuando hacemos scroll hacia arriba.

En ionic el evento de scroll se produce en el content, por tanto dicha barra ha de encontrarse dentro de ion-content.

Vamos a crear un proyecto de prueba para mostrar como funciona esta directiva:

ionic start hideMenu blank

Vamos a editar home.ts y le vamos a añadir un array con los items que mostraremos en el listado, por ejemplo una lista de videojuegos:

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

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

  items = [
    'Pokemon Yellow',
    'Super Metroid',
    'Mega Man X',
    'The Legend of Zelda',
    'Pac-Man',
    'Super Mario World',
    'Street Fighter II',
    'Half Life',
    'Final Fantasy VII',
    'Star Fox',
    'Tetris',
    'Donkey Kong III',
    'GoldenEye 007',
    'Doom',
    'Fallout',
    'GTA',
    'Halo',
    'Warcraft II'
  ];

  constructor(public navCtrl: NavController) {

  }

}

Ahora vamos a editar home.html y añadimos el siguiente código en amarillo en ion-content para generar la toolbar y los items de prueba:

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content hide-menu>

      <ion-toolbar no-border-top class='show-header'>
          <ion-segment color='secondary' [(ngModel)]="section">
            <ion-segment-button value="portada">
              Portada
            </ion-segment-button>
            <ion-segment-button  value="nuevas" >
              Nuevas
            </ion-segment-button>
            <ion-segment-button  value="populares">
              Populares
            </ion-segment-button>
            <ion-segment-button  value="mas_visitadas">
              + Visitadas
            </ion-segment-button>
          </ion-segment>
        </ion-toolbar>

    <ion-list>
      <button ion-item *ngFor="let item of items" (click)="itemSelected(item)">
        {{ item }}
      </button>
    </ion-list>
</ion-content>

Como podéis ver en la etiqueta ion-content hemos añadido hide-menu  que es el nombre de la directiva que vamos a crear.

Después hemos añadido una  ion-toolbar con ion-segment  y cuatro opciones que será la barra de menú que vamos a mostrar u ocultar.

Por ultimo mostramos un listado donde mostramos con *ngFor los items que hemos definido en el controlador.

Véase que de entrada la barra tiene la clase show-header. Incluiremos esta y hide-header en la listado.scss con la intención de hacerla aparecer y desaparecer respectivamente.

También necesitamos definir la posición de ion-tollbar como fixed.

Suponiendo que nuestro header principal (ion-header) tiene la altura 56px por defecto incluiremos lo siguiente en listado.scss:

page-home {

    ion-toolbar {
        position:fixed;
    }

    .hide-header{
        top: 0px;
    }

    .show-header{
        top: 56px;
    }
   
 }

Ya solo queda crear la directiva, para ello vamos a utilizar ionic generator:

ionic g directive hide-menu

Al generar la directiva nos habrá creado una carpeta directives y dentro estará la carpeta hide-menu con la directiva que acabamos de crear.

Necesitamos declarar la directiva en app.module.ts, por lo tanto lo editamos y añadimos lo siguiente:

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

import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { HideMenuDirective } from '../directives/hide-menu/hide-menu';

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

Ahora editamos el archivo hide-menu.ts de nuestra directiva  y vamos a importar e inyectar en el constructor ElementRef,  Renderer2, que necesitaremos para manipular los elementos de la vista:

import { Directive, ElementRef, Renderer2  } from '@angular/core';

/**
 * Generated class for the HideMenuDirective directive.
 *
 * See https://angular.io/api/core/Directive for more info on Angular
 * Directives.
 */
@Directive({
  selector: '[hide-menu]' // Attribute selector
})
export class HideMenuDirective {

  constructor(public element:ElementRef, public renderer:Renderer2) {
    console.log('Hello HideMenuDirective Directive');
  }

}

Vemos que en el decorador @Directive viene definido el selector que es el que tenemos que poner en el elemento de la vista al que queremos aplicar la directiva.

Ahora en la definición de la directiva a la propiedad host le vamos a asignar el evento ionScroll y le indicamos que ejecute la función handleScroll que definiremos después. Host hace referencia al elemento donde estamos aplicando la directiva, en este caso se lo estamos asignando al elemento ion-content:

@Directive({
  selector: '[hide-menu]', // Selector de la directiva
  host: {
    '(ionScroll)': 'handleScroll($event)' // Captura el evento scroll (ionScroll) dentro de ion content y envía el evento a su manejador handleScroll
  }
})

Ahora necesitamos definir tres variables:

toolBarRef – Hace referencia a la toolbar que vamos a mostrar u ocultar.
storedScroll – Guardará la posición del scroll.
threshold – Contendrá la distancia que tenemos que mover el scroll para que se oculte la barra.
export class HideMenuDirective {


  private toolBarRef:any;

  private storedScroll: number = 0;
  private threshold: number = 10;


  constructor(public element:ElementRef,
              public renderer:Renderer2) {
  }

Una vez definidas las variables en ngOnInit  hacemos que toolBarRef  referencie al elemento del DOM que contiene la barra,  ngOnInit se ejecuta una única vez al iniciar,  es parecido al constructor:

ngOnInit() {
    //Con esto conseguimos referenciar la barra de navegacion mediante su clase.
    this.toolBarRef = this.element.nativeElement.getElementsByClassName("toolbar")[0];
  }

Una vez hecho esto ya podemos definir la función handleScroll que como hemos indicado se ejecutará al hacer scroll sobre el elemento ion-content al que hemos asignado la directiva:

handleScroll(event) {
    if (event.scrollTop - this.storedScroll > this.threshold) {
      //al hacer scroll down hace desaparecer la barra mediante clases
      this.renderer.addClass(this.toolBarRef,'hide-header');
      this.renderer.removeClass(this.toolBarRef,'show-header');
    } else if (event.scrollTop - this.storedScroll < 0) {
       //al hacer scroll up la hace aparecer de nuevo
      this.renderer.removeClass(this.toolBarRef,'hide-header');
    }
    //actualizmos la ultima posicion conocida del scroll proveniente del evento
    this.storedScroll = event.scrollTop;
  }

En esta función comprobamos si la posición actual del scroll (event.scrollTop) menos la posición anterior es mayor que el limite (threshold), si es así es que estamos haciendo scroll hacia abajo por lo tanto con this.renderer.addClass le añadimos a la toolbar la clase hide-header que hará que se oculte la barra y con removeClass eliminamos la clase show-header que tenia.

En caso contrario significa que estamos haciendo scroll hacia arriba por lo tanto eliminamos la clase hide-header que oculta la barra, lo que hará que esta se vuelva a mostrar.

El código completo de la directiva quedaría así:

import { Directive, ElementRef, Renderer2 } from '@angular/core';

@Directive({
  selector: '[hide-menu]', //selector de la directiva
  host: {
    '(ionScroll)': 'handleScroll($event)' //Captura el evento scroll (ionScroll) dentro de ion content y envia el evento a su manejador handleScroll
  }
})
export class HideMenuDirective {

  //Varaibles para referenciar la barra 
  private toolBarRef:any;

  //Variables para saber la direccion del scroll
  private storedScroll: number = 0;
  private threshold: number = 10;


  constructor(public element:ElementRef,
              public renderer:Renderer2) {
  }

  ngOnInit() {
    //Con esto conseguimos referenciar la barra de navegacion mediante su clase.
    this.toolBarRef = this.element.nativeElement.getElementsByClassName("toolbar")[0];

  }

  handleScroll(event) {
    if (event.scrollTop - this.storedScroll > this.threshold) {
      //al hacer scroll down hace desaparecer la barra mediante clases
      this.renderer.addClass(this.toolBarRef,'hide-header');
      this.renderer.removeClass(this.toolBarRef,'show-header');
    } else if (event.scrollTop - this.storedScroll < 0) {
       //al hacer scroll up la hace aparecer de nuevo
      this.renderer.removeClass(this.toolBarRef,'hide-header');
    }
    //actualizmos la ultima posicion conocida del scroll proveniente del evento
    this.storedScroll = event.scrollTop;
  }
}

Si ejecutáis el ejemplo con ionic serve -l obtendreis algo como esto:

Bien, eso es todo por hoy, solo me resta agradecer a Miguel Escribano por su aporte y recomendaros que descarguéis su app meneame.

 

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

Mostrar un mapa offline en Ionic con Leaflet

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 posts anteriores vimos como mostrar un mapa de Google maps en ionic, sin embargo ¿que pasa si queremos mostrar información en un mapa sin conexión?, google maps no nos podrá mostrar el mapa.

En lugar de utlizar Google maps en esta ocasión vamos a utilizar Leaflet para mostrar un mapa.

Leaflet  es una biblioteca javascript de código abierto que nos permite crear mapas.

Lanzado por primera vez en 2011, es compatible con la mayoría de las plataformas móviles y de escritorio, y es compatible con HTML5 y CSS3 .

Es una de las bibliotecas de mapas de JavaScript más populares y es utilizada por los sitios web como FourSquare , Pinterest y Flickr .

Para poder mostrar un mapa offline es necesario tener alojados locálmente los titles que forman el mapa, si quieres mostrar un mapa de todo el mundo offline a día de hoy podemos afirmar  que es inviable ya que eso supondría muchísimos gigas de información, sin embargo si que puede ser viable mostrar un mapa offline de una ciudad.

Si por ejemplo estás desarrollando una aplicación de turismo de una ciudad concreta donde vas a mostrar en un mapa  los monumentos que merece la pena visitar en dicha ciudad, o cualquier otra información geolocalizada en un mapa, puede ser interesante el poder mostrar un mapa sin necesidad de tener conexión a Internet en ese preciso momento.

Vamos a ver como podemos solucionar esto en ionic 3, y como siempre la mejor manera de verlo es con un ejemplo.

Lo primero que vamos ha hacer es crear un nuevo proyecto en ionic que vamos a llamar mapaoffline:

ionic start mapaoffline blank

Una vez creado el en proyecto entramos en la carpeta con:

cd mapaoffline

Ahora vamos a descargar Leaflet, para ello descargamos la última versión desde  la sección de descargas de su página oficial en el siguiente enlace:

http://leafletjs.com/download.html

Se habrá descargado un archivo .zip, antes de nada debemos descomprimirlo para extraer su contenido:

 

Ahora vamos a crear una carpeta llamada leaflet en src/assets y dentro vamos a copiar la carpeta images y el archivo leaflet.css.

Una vez hecho esto vamos a cargar el archivo leaflet.css en index.html:

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
  <meta charset="UTF-8">
  <title>Ionic App</title>
  <meta name="viewport" content="viewport-fit=cover, 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">

  <!-- add to homescreen for ios -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">

  <!-- 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.error('Error', err));
    }
  -->

  <link href="build/main.css" rel="stylesheet">
  <link href="assets/leaflet/leaflet.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>

  <!-- The vendor js is generated during the build process
       It contains all of the dependencies in node_modules -->
  <script src="build/vendor.js"></script>


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

</body>
</html>

Ahora vamos a instalar leaflet desde consola con los siguiente comandos:

npm install leaflet --save
npm install @types/leaflet --save

Ahora que ya tenemos instalado leaflet necesitamos descargar los tiles del mapa que vamos a mostrar, para ello vamos a echar mano de una herramienta llamada Mobile Atlas Creator (MOBAC), para descargarla accedemos al siguiente enlace: http://mobac.sourceforge.net/ y bajamos en la página hasta encontrar Download y pulsamos en latest stable version para descargar la aplicación.

Una vez descargada tenemos que descomprimir el archivo zip y dentro encontraremos el archivo ejecutable.

Al ejecutar Mobile Atlas Creator nos pide introducir un nombre para el atlas (en este caso le llamamos simplemente mapa) y el formato, tenemos que seleccionar Osmdroid ZIP.

 

Pulsamos Aceptar y nos mostrará un mapa, podemos mover zoom para ver mejor la zona que queremos seleccionar. Si hacemos doble click en un punto el mapa hará zoom y se centrará en ese punto, por ello para localizar la zona que queremos mostrar es mejor partir de un zoom que nos permita ver donde estamos e ir haciendo doble click hasta obtener la localización y zoom que nos interesa.

En el panel de la izquierda en la sección map source tenemos los mapas de los que podemos obtener los tiles, muchos son solo de una zona en concreto, yo para este ejemplo voy a utilizar OpenStreetMap4UMaps.eu que nos ofrece un mapa de todo el mundo.

Debajo del panel Map Source tenemos  el panel Zoom Levels, aqui debemos seleccionar los niveles de zoom que queremos que tenga el mapa, cabe mencionar que cuantos más niveles de zoom mas tiles necesitaremos y por ende mas espacio ocuparán las imágenes.

Marcamos por ejemplo 11,12,13,14, y 15.

Ahora tenemos que seleccionar la porción del mapa que queremos descargar:

En la parte de arriba del mapa tenemos una barra de zoom que podemos mover para ver mejor la parte del mapa que queremos seleccionar. Con el ratón sobre el mapa seleccionamos un área.

Yo por ejemplo he seleccionado la zona de Bilbao, de está manera podré mostrar un mapa de Bilbao sin conexión.

Ahora debajo del panel Zoom Levels tenemos el panel Atlas content, aquí vamos a pulsar en el botón Add Selection lo que nos creará un Layer  que si lo desplegamos vemos que contiene dentro los niveles de zoom que hemos seleccionado.

Bien, una vez que hemos seleccionado el área que queremos descargar tenemos que seleccionar Create Atlas y acto seguido comenzará a generar el atlas:

El atlas que se ha generado será un archico .zip con el nombre que le hemos dado al atlas en este caso mapa seguido de la fecha y hora en la que ha sido generado y lo encontrarás en la carpeta atlases dentro de la carpeta  donde tengas Mobile Atlas Creator.

Bien, ahora vamos descomprimir el archivo zip que nos ha generado, al descomprimir vemos que dentro de la carpeta que nos ha creado hay otra carpeta llamada 4uMaps, vamos a cambiarle el nombre a la carpeta por simplemente mapa y a copiar esta carpeta dentro de la carpeta assets de nuestro proyecto:

 

Una vez que tenemos Los tiles copiados en la carpeta assets de nuestro proyecto ya podemos mostrarlos en el mapa.

Vamos a editar home.html e igual que hacíamos cuando creamos un mapa con google maps vamos a crear un div con id=”map” donde se renderizará el mapa:

<ion-header>
<ion-navbar>
  <ion-title>
    Ionic Blank
  </ion-title>
</ion-navbar>
</ion-header>

<ion-content padding>
<div id="map"></div>
</ion-content>

Ahora le vamos a definir el tamaño para que ocupe toda la pantalla por lo tanto editamos home.scss y añadimos lo siguiente:

page-home {
    #map{
        width:100%;
        height: 100%;
    }
}

Ahora en home.ts vamos a ver como se crea un mapa con leaflet:

Lo primero que debemos hacer es importar leaflet:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import * as L from 'leaflet';

Ahora vamos a crear una variable de clase llamada map donde guardaremos la instancia del mapa que vamos a crear:

...

export class HomePage {

  map : any;

  constructor(public navCtrl: NavController) {

  }

...

Ahora cuando se haya cargado la página en el método onViewDidLoad vamos a crear el mapa de la siguiente manera:

ionViewDidLoad() {

   this.map = L.map('map').
     setView([ 43.2603479, -2.9334110],
     12);

   L.tileLayer('assets/mapa/{z}/{x}/{y}.png', {    maxZoom: 15  }).addTo(this.map);

   L.marker([ 43.2603479,-2.9334110],{draggable: true}).addTo(this.map);

 }

Para crear un mapa con Leaflet utilizamos L.map(‘map’), ‘map’ es el id del div que hemos creado en la vista home.html y que es donde se va a renderizar el mapa.

Después con el metodo .setView le indicamos las coordenadas donde se tiene que posicionar el mapa, en este caso son las del centro de Bilbao.

Con L.tileLayer() añadimos al mapa una capa de tiles que va a contener los tiles del mapa que hemos descargado, como primer parámetro le pasamos la ruta donde se encuentran los tiles que puede ser una url si estamos utilizando un mapa online o la ruta donde se encuentrar alojados los archivos para mostrar el mapa offline como es el caso.

Si nos fijamos en la ruta vemos que es ‘assets/mapa/{z}/{x}/{y}.png’,   z, x e y hacen referencia respectivamente al nivel de Zoom, tiles en el eje X y tiles en el eje Y.

Obtendrán el valor correspondiente automáticamente en función de la posición del mapa y al tener la carpeta de los tiles ordenada por estos parámetros no te tienes que preocupar.

Despues con L.marker  creamos un marcador en en las coordenadas indicadas y le decimos que sea  arratrable (draggable) y con .addTo(this.map) lo añadimos a nuestro mapa.

El código completo de home.ts quedaría así:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import * as L from 'leaflet';

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

  map;

  constructor(public navCtrl: NavController) {

  }

  ionViewDidLoad() {

    this.map = L.map('map').
      setView([ 43.2603479, -2.9334110],
      12);

    L.tileLayer('assets/mapa/{z}/{x}/{y}.jpg', {    maxZoom: 15  }).addTo(this.map);

    L.marker([ 43.2603479,-2.9334110],{draggable: true}).addTo(this.map);

  }

}

Ahora si ejecutamos nuestra app aunque no tengamos conexión a internet podemos mostrar un mapa:

 

Eso es todo por hoy, como siempre espero que os sea de utilidad.

 

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

Firmar el apk para subirlo a Google Play

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,

Como el título del post indica hoy vamos a ver cómo generar un archivo .apk firmado para poder subirlo a google play.

Este es el último paso que debemos dar una vez tenemos desarrollada y lista nuestra aplicación para poder subirla a la tienda de aplicaciones de Google.

Android exige que todos los APK se firmen digitalmente con un certificado para poder instalarse.

Cuando firmas un APK se adjunta a este el certificado de clave pública que asocia el APK contigo y con tu clave privada correspondiente. Esto es necesario para poder lanzar cualquier actualización de la app asegurándose que provenga del autor original, por eso es importantísimo que guardemos el archivo keystore generado a buen recaudo para poder lanzar futuras actualizaciones ya que todas las aplicaciones deben usar el mismo certificado durante su vida útil.

Un keystore es un campo binario que contiene una o más claves privadas.

Si desarrollas una app nativa puedes generar el keystore firmar el apk desde Android studio, nosotros vamos a ver como firmar una aplicación   generada desde ionic o apache cordova desde consola.

Para poder firmar el apk desde consola debemos seguir una serie de pasos que vamos a ver a continuación:

Lo primero que necesitamos es generar  el keystore:

Si por ejemplo nuestra app se llama miapp para generar un keystore  abrimos  una terminal, nos situamos en la carpeta de nuestro proyecto y escribimos el siguiente comando:

keytool -genkey -v -keystore miapp.keystore -alias miapp -keyalg RSA -keysize 2048 -validity 10000

miapp.keystore es el archivo que se va a generar, el alias es un nombre de identificación para tu clave, en este caso le hemos llamado miapp.

Con keysize le indicamos que el tamaño de la clave sea de 2048 bits, es recomendavle dejarlo en este valor.

Por último con -validity fijamos el período de validez de tu clave en años, creo que  con 10000 años será suficiente ;-P.

Al ejecutar el comando nos pide que introduzcamos una serie de datos:

Introduzca la contraseña del almacén de claves: 

Volver a escribir la contraseña nueva: 

¿Cuáles son su nombre y su apellido?

¿Cuál es el nombre de su unidad de organización?

¿Cuál es el nombre de su organización?

¿Cuál es el nombre de su ciudad o localidad?

¿Cuál es el nombre de su estado o provincia?

¿Cuál es el código de país de dos letras de la unidad?

Te pide dos veces la contraseña, es importante que recuerdes la contraseña que has introducido ya que la necesitarás después.

Al final te muestra los datos que has introducido y te pide confirmación, le decimos que si:

¿Es correcto CN=Eduardo, OU=Revilla, O=revigames, L=Abadiño, ST=Bizkaia, C=ES?

  [no]:  Si

Lo que os sale debe ser algo parecido a la siguiente imagen:

Bien, con esto ya hemos generado un archivo llamado miapp.keystore en la raiz de nuestro proyecto.

Ahora vamos a crear un nuevo archivo en la raiz del proyecto llamado build.json, lo creamos con nuestro editor de código y dentro escribimos el siguiente código:

{
     "android": {
         "debug": {
             "keystore": "miapp.keystore",
             "storePassword": "XXXXX",
             "alias": miapp",
             "password" : "XXXX",
             "keystoreType": ""
         },
         "release": {
             "keystore": "miapp.keystore",
             "storePassword": "XXXX",
             "alias": "miapp",
             "password" : "XXXX",
             "keystoreType": ""
         }
     }
 }

El primero es para la versión de debug y “release” es para la versión final que vas a subir a la play store.

En el campo keystore debes poner la ruta del archivo .keystore que acabamos de generar, si lo has generado en la raíz solo debes de poner el nombre del archivo, en este caso miapp.keystore.

En alias ponemos el alias que hemos puesto al crear el archivo

Después en storePassword debe poner la contraseña del almacén de claves que indicaste al crear el archivo.

En password debemos poner la contraseña.

Guardamos el archivo y ya solo nos queda generar el apk firmado con el siguiente comando:

ionic cordova build android --release

Si no ha detectado errores y se ha compilado bien podrás encontrar el apk firmado que debes subir a play store en la siguiente ruta dentro de tu aplicación:

/platforms/android/build/outputs/apk/android-release.apk

Eso es todo por hoy, espero que os haya sido de utilidad y ver vuestras apps triunfando en la play store pronto ;-).

Aprovecho para deciros que si has creado alguna app con ionic y la tienes en la play store puedes compartirlo en los comentarios, así servirá como inspiración para que otras personas puedan ver lo que se puede crear con ionic y al mismo tiempo seguro que ganáis alguna descarga extra que siempre viene bien 😉

 

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

Libro: Desarrollo de aplicaciones móviles multiplataforma y PWAs con Ionic y Firebase desde cero.

Hola a todos,

Como diría Francisco Umbral, ¡He venido a hablar de mi libro! ;-P

Hoy vengo a anunciaros que por fin he publicado en Amazon mi libro para aprender a programar aplicaciones multiplataforma con ionic.

Ionic es un framework que nos permite crear de una manera rápida y sencilla aplicaciones móviles multiplataforma (Android, IOS, Windows, PWAs…) utilizando tecnologías web (HTML, JAVASCRIPT, CSS), por lo que si eres desarrollador web podrás reciclar tus conocimientos y crear aplicaciones móviles de una manera sencilla.

En este libro aprenderás de una manera sencilla y con una curva de aprendizaje suave todo lo necesario para crear aplicaciones móviles con ionic desde cero.

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 el 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.

El libro está en español por lo que será de especial utilidad para aquellos que no se arreglen bien con el inglés.

El propósito del libro es hacer la curva de aprendizaje lo más suave posible, empezando a desarrollar cosas prácticas desde el principio y aprendiendo cosas nuevas según las vamos necesitando en lugar de abrumaros con mucha teoría al principio sin ver su aplicación práctica, ya 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.

Para que te hagas una idea del contenido del libro te muestro el índice con el contenido del mismo:

  • Introducción
    • ¿Qué es ionic?
    • Ventajas de utilizar ionic para desarrollar apps
    • Desventajas de utilizar ionic para desarrollar apps
    • Diferencia entre Ionic 1, ionic 2 y 3 e Ionic 4
  • Cómo obtener el código de los ejemplos de este libro
  • Instalar ionic y las herramientas necesarias para el desarrollo
    • Instalar Node.js
    • Instalar ionic
    • Instalar Android studio.
    • En windows
    • Instalar Android Studio en Mac
    • Instalar un editor de código compatible con TypeScript
  • Hola Mundo en Ionic
  • Estructura de un proyecto Ionic
  • Modificando nuestro hola mundo
  • Mini Juego de acertar números
  • Personalizando el diseño de nuestra aplicación
    • Variables SASS y Variables CSS4
    • Colores
    • Utilidades CSS – Estilos predefinidos
    • Alineación del texto
    • Transformación de texto
    • Atributos de Texto según tamaño de pantalla
    • Emplazamiento de elementos
    • Elementos float
    • Elementos Flotantes según tamaño de pantalla
    • Visualización de elementos
    • Visualización u ocultación de elementos en función del tamaño de la pantalla
    • Espaciado del contenido
    • Padding
    • Margin
  • Navegación
    • Ionic Generator
    • Navegar entre páginas utilizando routerLink en la plantilla html
    • Navegar entre páginas programáticamente
    • Volver a la página anterior
    • Pasando datos entre páginas
    • Menú lateral
    • Navegación por Tabs
  • Pipes
  • Creando una aplicación para guardar nuestros sitios geolocalizados
    • Descripción de la aplicación
    • Mostrando el mapa
    • Promesas y Async/Await
    • Iniciando el mapa
    • Añadiendo un marcador al mapa
    • Añadiendo FAB
    • Modales
    • Creando el formulario
    • Utilizando la cámara
    • Creando un Service
    • Probando nuestra aplicación misSitios en el móvil
    • Ejecutar nuestra aplicación en un dispositivo android:
    • Ejecutar nuestra aplicación en un dispositivo iOS:
    • Guardando nuestros sitios en una base de datos local
    • Ionic storage
    • Mostrar el detalle del sitio
    • Modificar nuestros sitios
    • Eliminar un sitio desde el listado deslizando el item con “ItemSliding”
    • Alerts
  • Preparar la app para la publicación usando capacitor
    • Generar el splash screen y los iconos en Android
    • Generar el splash screen y los iconos en iOS
    • Generar ejecutable firmado para poder subirlo a google play.
  • Ionic Native y Capacitor
  • Componentes personalizados
  • Peticiones http
    • http.get
    • Promesas y Observables
    • http.post
  • Firebase
    • Autenticación con correo y contraseña
    • Alta de usuarios
    • Login de usuarios
    • Guards: Guardián de navegación
    • Logout
    • Database: Cloud Firestore – Guardar nuestros sitios en la nube
    • Obtener listado de sitios de firestore.
  • Apache Cordova
    • Añadiendo plataformas
    • Ejecutando nuestra app en el dispositivo móvil
    • Ionic Native
    • Preparar la app para la publicación usando cordova
    • Generar el splash screen y los iconos
    • Configurando nuestra app: Modificando el archivo config.xml
    • Evitar rotación de pantalla
    • Firmar el apk para subirlo a Google Play
  • Generar una PWA (Aplicación Web Progresiva)
    • ¿Que es una PWA?
    • Creando un hola mundo de ejemplo
    • Configurar la PWA
    • Preparar la aplicación para el entorno de producción
    • Alojar nuestra aplicación en Firebase Hosting:
  • Ahora te toca a tí

Si te ha gustado el contenido sobre ionic que he compartido en este blog y quieres tenerlo más completo y mejor organizado y de paso apoyarme ya puedes comprar este libro en amazon:

Lo puedes encontrar en versión tapa blanda:

https://www.amazon.es/dp/1700622722

También está disponible en Kindle:

 

No quiero aburriros más así que muchas gracias a todos los que me habéis apoyado 🙂

Un saludo y hasta el próximo post.

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

Tutorial de Ionic – Crear una aplicación para guardar nuestros sitios geolocalizados – Parte 7 – Modificar nuestros sitios.

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

 

¡¡Atención!! este tutorial se basa en ionic 3 y está desactualizado por lo que es posible que los ejemplos no funcionen en la última versión de ionic, haz click aquí para acceder a un tutorial mas actual de Ionic.

Hola a todos,

En el último post vimos como mostrar en un modal los sitios guardados y como abrir el navegador GPS para que nos marque la ruta hasta el.

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

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

...

export class ModalDetalleSitio {

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

...

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

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

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

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

<!--
  Generated template for the ModalDetalleSitio page.

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

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

</ion-header>


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

Vayamos por partes:

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

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

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

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

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

 <ion-card *ngIf="edit">

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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



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

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

...

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

editar(){
   this.edit = true;
 }

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

sacarFoto(){

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


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

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

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

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

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

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

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

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

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

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

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

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

/*
  Generated class for the DbProvider provider.

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

  db : SQLiteObject = null;

  constructor(public sqlite: SQLite) {
    console.log('Hello DbProvider Provider');
  }

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

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

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

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

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

}

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

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

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

  sitio: any;
  edit : boolean = false;


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

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

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

  comoLlegar(){

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

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

 }

 editar(){
   this.edit = true;
 }

 sacarFoto(){

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


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

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


}

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

 

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

Tutorial de Ionic – Crear una aplicación para guardar nuestros sitios geolocalizados – Parte 6 – Mostrar detalles del sitio guardado y abrir navegador gps para llegar a el.

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

 

¡¡Atención!! este tutorial se basa en ionic 3 y está desactualizado por lo que es posible que los ejemplos no funcionen en la última versión de ionic, haz click aquí para acceder a un tutorial mas actual de Ionic.

Hola a todos, en el post anterior vimos como guardar nuestros sitios en la base de datos y como mostrar los sitios que tenemos guardados en la página listado.

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

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

ionic g page modalDetalleSitio

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

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

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

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

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

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

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

<!--
  Generated template for the Listado page.

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

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

</ion-header>


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

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

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

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

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

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

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

...

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

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

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

...
export class ModalDetalleSitioPage {

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

...

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

<!--
  Generated template for the ModalDetalleSitio page.

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

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

</ion-header>


<ion-content padding>
  <ion-card>
    <img [src]="sitio.foto" *ngIf="sitio.foto" />
    <ion-card-content>
       <ion-item text-wrap>
          <h2>{{ sitio.address }}</h2>
           <p>{{ sitio.description }}</p>
        </ion-item>
    </ion-card-content>
    <ion-row>
      <ion-col text-center>
         <button ion-button icon-left clear small (click)="comoLlegar()">
           <ion-icon name="navigate"></ion-icon>
           <div>Como llegar</div>
         </button>
      </ion-col>
    </ion-row>
</ion-card>
</ion-content>

Como título del modal hemos puesto la dirección que está almacenada en sitio.address.

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

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

Después dentro de ion-card-content  mostramos la dirección y la descripción.

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

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

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

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

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

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

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

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

Ahora debemos importar y declarar el plugin en app.module.ts:

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

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

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

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

  ]
})
export class AppModule {}

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

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

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

  sitio: any;

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

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

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

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

 }
}

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

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

Modal detalle del sitio
Modal detalle del sitio

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

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

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

 

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

Tutorial de Ionic – Crear una aplicación para guardar nuestros sitios geolocalizados – Parte 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 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: