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 鈥淗ello 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 directivay 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 鈥淚temSliding鈥
    • 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 dentromostramos la foto solo si existe con el condicional *ngIf.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  ]
})
export class AppModule {}

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

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

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

  sitio: any;

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

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

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

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

 }
}

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

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

Modal detalle del sitio
Modal detalle del sitio

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

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

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

 

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

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

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

 

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

Hola a todos:

En el post anterior vimos como poner un marcador al mapa, aprendimos a utilizar los botones FAB y aprendimos a utilizar ventanas modales.

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

Bien, vayamos por partes:

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

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

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

...

export class ModalNuevoSitioPage {

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

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

...

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

Vamos a ver brevemente como funciona Google Maps 聽Geodecoder:

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

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

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

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

Creando nuestras propias promesas

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

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

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

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

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

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

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

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

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

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

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

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

Mostrando las coordenadas y la 聽direcci贸n.

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

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

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

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

</ion-header>

<ion-content padding>
  <ion-card>
    <ion-card-header>
      Localizaci贸n actual
    </ion-card-header>
    <ion-card-content>
      <p><strong>lat:</strong>{{ coords.lat}}<br/>
      <strong>lng:</strong>{{coords.lng }}</p>
      <hr>
      <p>{{ address }}</p>
    </ion-card-content>
  </ion-card>
</ion-content>

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

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

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

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

Creando el formulario.

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

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

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

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

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

</ion-header>

<ion-content padding>
  <ion-card>
    <ion-card-header>
     <strong>Localizaci贸n actual</strong>
    </ion-card-header>
    <ion-card-content>
      <p><strong>lat:</strong>{{ coords.lat}}<br/>
      <strong>lng:</strong>{{coords.lng }}</p>
      <hr>
      <p>{{ address }}</p>
      <hr/>
      <form (ngSubmit)="guardarSitio()">
      <ion-item>
        <img [src]="foto" *ngIf="foto" />
        <button ion-button icon-left full type="button" (tap)="sacarFoto()">
          Foto&nbsp;&nbsp;
          <ion-icon name="camera"></ion-icon>
        </button>
      </ion-item>
      <hr/>
      <ion-item>
        <ion-label>Descripci贸n</ion-label>
        <ion-textarea [(ngModel)]="description" name="description"></ion-textarea>
      </ion-item>
      <button ion-button type="submit" block>Guardar Sitio</button>
    </form>
    </ion-card-content>
    </ion-card>
</ion-content>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

sacarFoto(){

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


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

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

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

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

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

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

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

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

declare var google:any;

/*
聽聽Generated class for the ModalNuevoSitio page.

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

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

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

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

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

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

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

  sacarFoto(){

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


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

}

A帽adiendo plataformas

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

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

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

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

ionic cordova platform add android

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

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

Ejecutando nuestra app en el dispositivo m贸vil

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

ionic cordova run android

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

ionic cordova emulate android

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

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

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

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

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

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

 

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

Tutorial de Ionic – Crear una aplicaci贸n para guardar nuestros sitios geolocalizados – Parte 3: A帽adiendo FAB, marcador y ventana modal.

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

 

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

Hola a todos:

En el post anterior聽vimos como insertar en nuestra app un mapa de google maps centrado en las coordenadas actuales.
Vamos a continuar desarrollando nuestra app.

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

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

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

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

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

loadMap(){

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

}

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

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

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

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

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

Vayamos por partes.

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

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

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

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

</ion-header>

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

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

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

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

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

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

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

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

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

Modales

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

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

ionic g page modalNuevoSitio

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

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

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

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

    ......

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

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

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

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

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

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

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

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

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


/*
聽聽Generated class for the ModalNuevoSitio page.

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

  coords : any = { lat: 0, lng: 0 }

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

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

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

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

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

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

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

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


/*
聽聽Generated class for the ModalNuevoSitio page.

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

  coords : any = { lat: 0, lng: 0 }
  constructor(public navCtrl: NavController, public navParams: NavParams, private viewCtrl : ViewController ) {}

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

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

}

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

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

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

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

</ion-header>

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

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

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

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