Archivo de la etiqueta: curso ionic 2 español

Como crear componentes personalizados con Ionic

 

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

Hola a todos:

En posts anteriores hemos aprendido a crear nuestras apps multiplataforma con Ionic.
Ionic nos ofrece un montón de componentes ya creados para utilizar en nuestras apps y realmente nos permiten con poco esfuerzo crear una interfaz funcional para nuestras aplicaciones.

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

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

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

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

ionic start miComponente blank

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

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

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

  constructor(public navCtrl: NavController) {

  }

}

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

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

ionic g component saluda

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

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

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

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

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

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

  text: string;

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

}

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

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

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

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

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


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

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

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

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

Así de fácil.

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

 

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

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

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

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

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

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

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

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

}

Para poder utilizar el decorador Input debemos importarlo primero.

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

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

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

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

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

}

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

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

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

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

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


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


}

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

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

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

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

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

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

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

  constructor(public navCtrl: NavController) {

  }

}

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

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

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

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

Si probamos este ejemplo veremos algo como esto:

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

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

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

Como crear una app multi idioma con Ionic

 

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

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

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

ionic start ejemploidioma blank --cordova

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { TranslateService } from '@ngx-translate/core';

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

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

  constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen, private translateService: TranslateService) {
    platform.ready().then(() => {
      // Okay, so the platform is ready and our plugins are available.
      // Here you can do any higher level native things you might need.
      this.translateService.setDefaultLang('es');
      this.translateService.use('es');

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

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

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

En en.json pondremos  lo siguiente:

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

 

Y en es.json:

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

 

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

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

Ya estamos preparados para traducir  nuestra app.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  }

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

}

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

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

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

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

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

 

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

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

 

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

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

Hola a todos:

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

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

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

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

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

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

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

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

ionic start hideMenu blank

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

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

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

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

  constructor(public navCtrl: NavController) {

  }

}

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

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

<ion-content hide-menu>

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

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

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

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

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

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

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

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

page-home {

    ion-toolbar {
        position:fixed;
    }

    .hide-header{
        top: 0px;
    }

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

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

ionic g directive hide-menu

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

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

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

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

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

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

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

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

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

}

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

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

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

Ahora necesitamos definir tres variables:

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


  private toolBarRef:any;

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


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

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

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

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

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

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

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

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

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

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

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

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


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

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

  }

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

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

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

 

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

Mostrar un mapa offline en Ionic con Leaflet

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

 

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

Hola a todos:

En posts anteriores vimos como mostrar un mapa de Google maps en ionic, sin embargo ¿que pasa si queremos mostrar información en un mapa sin conexión?, google maps no nos podrá mostrar el mapa.

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

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

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

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

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

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

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

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

ionic start mapaoffline blank

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

cd mapaoffline

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

http://leafletjs.com/download.html

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

 

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

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

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

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

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

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

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

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


</head>
<body>

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

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

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


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

</body>
</html>

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

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

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

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

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

 

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

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

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

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

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

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

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

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

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

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

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

 

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

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

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

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

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

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

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

Lo primero que debemos hacer es importar leaflet:

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

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

...

export class HomePage {

  map : any;

  constructor(public navCtrl: NavController) {

  }

...

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

ionViewDidLoad() {

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

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

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

 }

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

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

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

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

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

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

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

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

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

  map;

  constructor(public navCtrl: NavController) {

  }

  ionViewDidLoad() {

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

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

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

  }

}

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

 

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

 

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

Firmar el apk para subirlo a Google Play

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

 

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

Hola a todos,

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

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

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

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

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

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

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

Lo primero que necesitamos es generar  el keystore:

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

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

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

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

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

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

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

Volver a escribir la contraseña nueva: 

¿Cuáles son su nombre y su apellido?

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

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

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

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

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

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

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

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

  [no]:  Si

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

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

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

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

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

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

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

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

En password debemos poner la contraseña.

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

ionic cordova build android --release

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

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

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

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

 

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

Tutorial de Ionic – Peticiones http – API REST

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

 

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

 

Hola a todos,  hoy vamos a ver como podemos comunicar una aplicación desarrollada con ionic con una API REST, para ello vamos a aprender como realizar peticiones a un servidor remoto a través de http.

En esta pequeña prueba vamos a ver como podemos acceder a una API REST para obtener desde un servidor remoto un listado de usuarios. Para este pequeño ejemplo vamos a utilizar RANDOM USER GENERATOR que como se indica en su web es una API libre de código abierto para generar datos de usuario aleatorios para realizar pruebas. Como Lorem Ipsum, pero con personas.

Lo que vamos a hacer es simplemente realizar una llamada a esta API donde recibiremos como respuesta un listado de usuarios que mostraremos en nuestra vista.

Antes de nada vamos a crear una nueva aplicación de prueba:

ionic start pruebahttp1 blank

Ahora vamos a crear un provider donde gestionaremos la comunicación con el servidor remoto:

ionic g provider http

Se habrá creado una carpeta providers (si no existía) y dentro una carpeta con el nombre del provider que acabamos de crear (http) y dentro un archivo .ts con el mismo nombre.

Por defecto contendrá el siguiente código:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

/*
  Generated class for the HttpProvider provider.

  See https://angular.io/guide/dependency-injection for more info on providers
  and Angular DI.
*/
@Injectable()
export class HttpProvider {

  constructor(public http: HttpClient) {
    console.log('Hello HttpProvider Provider');
  }

}

Vemos que al crear un provider ya se importa el módulo HttpClient.

Para poder utilizar HttpClient tenemos que importar y declarar HttpClientModule en el archivo app.module.ts:

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

import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { HttpProvider } from '../providers/http/http';
import { HttpClientModule } from '@angular/common/http';



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

http.get

Ahora vamos a añadir un método que llamaremos loadUsers a nuestro provider para obtener la lista de usuarios desde el servidor, por lo tanto editamos el archivo http.ts y añadimos el siguiente código:
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

/*
  Generated class for the HttpProvider provider.

  See https://angular.io/guide/dependency-injection for more info on providers
  and Angular DI.
*/
@Injectable()
export class HttpProvider {

  path : string = 'https://randomuser.me/api/?results=25';
  
  constructor(public http: HttpClient) {
    console.log('Hello HttpProvider Provider');
  }

  loadUsers(){
    return this.http
    .get(this.path)
  }
}
http.get devuelve el resultado de la solicitud en la forma de un observable.
El listado de usuarios tendrá un formato similar a este:
{
  "results": [
    {
      "gender": "male",
      "name": {
        "title": "mr",
        "first": "romain",
        "last": "hoogmoed"
      },
      "location": {
        "street": "1861 jan pieterszoon coenstraat",
        "city": "maasdriel",
        "state": "zeeland",
        "postcode": 69217
      },
      "email": "romain.hoogmoed@example.com",
      "login": {
        "username": "lazyduck408",
        "password": "jokers",
        "salt": "UGtRFz4N",
        "md5": "6d83a8c084731ee73eb5f9398b923183",
        "sha1": "cb21097d8c430f2716538e365447910d90476f6e",
        "sha256": "5a9b09c86195b8d8b01ee219d7d9794e2abb6641a2351850c49c309f1fc204a0"
      },
      "dob": "1983-07-14 07:29:45",
      "registered": "2010-09-24 02:10:42",
      "phone": "(656)-976-4980",
      "cell": "(065)-247-9303",
      "id": {
        "name": "BSN",
        "value": "04242023"
      },
      "picture": {
        "large": "https://randomuser.me/api/portraits/men/83.jpg",
        "medium": "https://randomuser.me/api/portraits/med/men/83.jpg",
        "thumbnail": "https://randomuser.me/api/portraits/thumb/men/83.jpg"
      },
      "nat": "NL"
    }
  ],
  "info": {
    "seed": "2da87e9305069f1d",
    "results": 1,
    "page": 1,
    "version": "1.1"
  }
}
Ahora vamos a crear la vista en home.html para mostrar un botón que llamará a la función
cargarUsuarios, y un listado de items con los usuarios que crearemos recorriendo con *ngFor el array usuarios que posteriormente vamos a crear en el controlador:
<ion-header>
  <ion-navbar>
    <ion-title>
      Usuarios
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content>
  <ion-list>
    <ion-item *ngFor="let usuario of usuarios">
      <ion-avatar item-start>
        <img [src]="usuario.picture.medium">
      </ion-avatar>
      <h2>{{ usuario.name.first }}</h2>
      <p>{{ usuario.email }}</p>
    </ion-item>
  </ion-list>
  <button ion-button full (click) = "cargarUsuarios()">Cargar Usuarios</button>
</ion-content>
Ahora vamos a modificar home.ts para obtener los datos desde el provider y mostrarlos en la vista.
Editamos home.ts e importamos el provider httpProvider que acabamos de crear:
import { HttpProvider } from '../../providers/http/http';
Para poder utilizarlo debemos inyectarlo en el constructor:
constructor(public navCtrl: NavController, public http: HttpProvider) {

  }
Justo antes del constructor vamos a definir una variable miembro donde guardaremos el array de usuarios que recibamos desde el servidor:
usuarios : any[];

Promesas y Observables

Ahora vamos a crear un método que que a su vez llamará al método loadUsers de nuestro provider para recibir los datos de los usuarios:
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { HttpProvider } from '../../providers/http/http';

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

  usuarios : any[];

  constructor(public navCtrl: NavController, public http: HttpProvider) {

  }

  cargarUsuarios(){
    this.http.loadUsers().subscribe(
      (res) => { 
        this.usuarios = res['results'];
      },
      (error) =>{
        console.error(error);
      }
    )
  }


}

Como podemos observar llamamos al método loadUsers que hemos definido en el provider, pero no utilizamos then sino subscribe, esto es porque http no devuelve una promesa si no que devuelve un observable. Un observable se queda a la espera de recibir datos y nosotros nos “subscribimos” recibiendo estos datos en cuanto estén disponibles.
Esta cualidad se puede utilizar para suscribirnos a la url y observar si ha habido cambios, como por ejemplo si es un sitio de noticias donde se están continuamente renovando.

La diferencia entre promesas y observables a groso modo es que la promesa devuelve los datos una única vez cuando estos son recibidos mientras que un observable se queda “vigilando” si se han producido cambios y se ejecuta cada vez que un cambio se produce, aunque hasta que no te suscribes a un observable éste no se ejecutará.

Si solo necesitamos recibir los datos una única vez sin necesidad de observar si se han producido cambios podemos utilizar promesas. Una promesa se ejecuta una vez que se haya resuelto la llamada y recibimos los datos en la función .then(res=>{… }).

El código del provider en el archivo http.ts utilizando una promesa quedaría de la siguiente manera:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import 'rxjs/add/operator/toPromise';

/*
  Generated class for the HttpProvider provider.

  See https://angular.io/guide/dependency-injection for more info on providers
  and Angular DI.
*/
@Injectable()
export class HttpProvider {

  path : string = 'https://randomuser.me/api/?results=25';
  
  constructor(public http: HttpClient) {
    console.log('Hello HttpProvider Provider');
  }

  loadUsers(){
    return this.http
    .get(this.path)
    .toPromise();

  }  
}

Debemos importar el operador toPromise para convertir el observable en promesa  con import ‘rxjs/add/operator/toPromise’;  y simplemente añadir toPromise() después del get.

Ahora en home.ts solo tenemos que sustituir subscribe por then:

cargarUsuarios(){
  this.http.loadUsers().then(
    (res) => { 
      this.usuarios = res['results'];
    },
    (error) =>{
      console.error(error);
    }
  )
}

Si ejecutáis la aplicación con ionic serve -l y pulsáis en el botón Cargar Usuarios podréis ver algo como esto:

Listado de usuarios
Listado de usuarios

Podéis observar como cada vez que pulséis el botón la lista de usuarios cambia ya que Random User Generator como su propio nombre indica devuelve una lista aleatoria de usuarios.

http.post

RANDOM USER GENERATOR  nos ha servido para aprender ha hacer una petición get a una API REST para recibir datos, en este caso una lista de usuarios.

Si queréis conectaros con un servicio que tengáis corriendo en un servidor remoto y necesitáis pasarle datos desde la aplicación para que se guarden en el servidor tenemos que usar http.post.

Vamos a imaginar que tenemos corriendo un servicio en PHP cuya url sea www.miservicio.com/adduser/ que está programado para recibir vía post un nuevo usuario para guardarlo en la base de datos del servidor.

Tendremos que crear  una función similar a esta:

postDatos(){
  let datos = { nombre:'Edu',email:'edu.revilla.vaquero@gmail.com'}
  
  let options = {
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  };
 var url = 'www.miservicio.com/adduser/';
 return new Promise(resolve => {
  this.http.post(url,JSON.stringify(datos),options)
     .subscribe(data => {
       resolve(data);
      });
 });
}

En la variable datos  tenemos un objeto con los datos del usuario que queremos enviar al servidor, en este caso nombre y email.

Después definimos la variable options donde a su vez definimos las cabeceras en la variable headers, dependiendo de la configuración del servidor podría necesitar parámetros diferentes en la cabecera.

Como vemos http.post es bastante parecido a http.get, solo que como segundo parámetro le pasamos la variable que contiene los datos que queremos enviar convertida a formato JSON con JSON.stringify(datos), como tercer parámetro le pasamos options.

En data recibiremos la respuesta que nos de el servidor.

Vamos a ver un ejemplo de como obtendríamos los datos enviados desde nuestra aplicación con un servicio desarrollado en PHP en el servidor:

<?php
$postdata = file_get_contents("php://input");
if (isset($postdata)) {
$request = json_decode($postdata);
echo $request->nombre;
echo $request->email;
}
?>

Evidentemente en otros lenguajes de programación del lado del servidor el código para recibir los datos sería diferente, pero se escapa del propósito de este tutorial el abordar como sería en cada leguaje. Si alguno hace la prueba en otro lenguaje y quiere compartirlo en los comentarios para ayudar a los demás será bien recibido :-).

Eso es todo por hoy.

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

Tutorial de Ionic – Crear una aplicación para guardar nuestros sitios geolocalizados – Parte 8 – Eliminar un sitio desde el listado deslizando el item con "ItemSliding".

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

 

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

Hola a todos.

En el post anterior  vimos como modificar un sitio, hoy vamos a mejorar nuestra app para que se puedan eliminar nuestros sitio, pero para aprender más sobre las posibilidades de ionic lo vamos ha hacer desde el listado, deslizando el elemento del listado hacia la izquierda nos mostrará el botón de eliminar. También aprovecharemos para aprender a utilizar AlertController para mostrar un dialogo que pida confirmación antes de eliminar el sitio.

Vamos a ver como sería:

Lo primero que vamos a hacer es modificar el listado para añadir un ion-item-sliding que es un componente que nos permite deslizar el item, debemos introducir el item dentro de ion-item-sliding y le vamos a añadir despues del item un elemento ion-item-options que nos permite añadir opciones que se mostrarán al deslizar el item. Se pueden añadir tantas opciones como desees, nosotros solo vamos a necesitar una para borrar el elemento. Vamos a ver como tiene que quedar el código, editamos el archivo listado.html y lo dejamos de la siguiente manera:

<!--
  Generated template for the ListadoPage page.

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

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

</ion-header>


<ion-content padding>
<ion-list>
  <ion-item-sliding #item *ngFor="let sitio of sitios">
    <ion-item (click)="muestraSitio(sitio)">
      <ion-thumbnail item-left>
        <img [src]="sitio.foto">
      </ion-thumbnail>
      <h2>{{ sitio.address }}</h2>
      <p>{{ sitio.description }}</p>
    </ion-item>
    <ion-item-options side="right">
      <button ion-button  color="danger" (click)="borrarSitio(sitio.id)"><ion-icon name="trash"></ion-icon>
         Borrar
      </button>
    </ion-item-options>
  </ion-item-sliding>
</ion-list>
</ion-content>

Como podemos observar hemos puesto el *ngFor en la etiqueta ion-item-sliding que ahora envuelve al ion-item que ya teníamos.

Despues del ion-item hemos añadido un componente ion-item-options al que le indicamos que se muestre en el lado derecho (side=”right”).

Dentro de ion-item-options tenemos un botón con in icono (ion-icon) que muestra el icono “trash” que es el típico cubo de basura y el texto “borrar”.
En el evento click del botón le decimos que llame a la función borrarSitio.

Si probamos la aplicación podéis comprobar en el listado que los items ahora se pueden arrastrar hacia la izquierda y aparece el botón de borrar:

Borrando un sitio
Borrando un sitio

Ahora debemos definir la función borrar sitio en el controlador del listado. Antes de borrar vamos a sacar un dialogo de confirmación que nos preguntará si realmente queremos eliminar el sitio, para ello vamos a utilizar el componente Alert.

Alerts

Los alerts son una excelente manera de ofrecer al usuario la posibilidad de elegir una acción específica o una lista de acciones. También pueden proporcionar al usuario información importante, o requerir que tomen una decisión.

Ionic nos proporciona los siguientes tipos de Alerts:

En este caso  como lo que queremos es que el usuario confirme el borrado del sitio vamos a utilizar alert de confirmación.

Para poder utilizar un alert lo primero que debemos hacer es importar AlertController e inyectarlo en el constructor, por lo tanto vamos a editar el archivo listado.ts y añadimos el siguiente código:

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


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

  sitios: any;
  constructor(public navCtrl: NavController,
              public navParams: NavParams,
              public modalCtrl : ModalController,
              public db : DbProvider,
              public alertCtrl : AlertController) {
  }

....

Ahora  vamos a  a crear la función borrarSitio() y definiremos dentro de ella el alert de la siguiente manera:

borrarSitio(id){

    let alert = this.alertCtrl.create({
      title: 'Confirmar borrado',
      message: '¿Estás seguro de que deseas eliminar este sitio?',
      buttons: [
        {
          text: 'No',
          role: 'cancel',
          handler: () => {
            // Ha respondido que no así que no hacemos nada
          }
        },
        {
          text: 'Si',
          handler: () => {
               // AquÍ borramos el sitio en la base de datos

           }
        }
      ]
    });

    alert.present();

 }

Para crear un alert utilizamos el método create donde tenemos que definir el título en este caso “Confirmar borrado”, el mensaje: “¿Estas seguro de que deseas eliminar este sitio?”.

Después definimos los dos botones de acción uno para SI y otro para NO, en text definimos el texto del botón y en handler definiremos la acción que debemos realizar, en caso de pulsar “No” no hacemos nada, en caso de pulsar “Si” procederemos a eliminar el sitio.

Una vez definido el alert con alert.present() hacemos que se muestre.

Ahora que ya sabemos como se usan los alerts vamos a escribir el código del botón “Si” para borrar el sitio.

Lo primero que tenemos que hacer es crear un nuevo método en el provider DbProvider para eliminar los sitios, por lo tanto editamos el archivo db.ts y añadimos la función borrarSitio():

public borrarSitio(id){
     let sql = "DELETE FROM sitios WHERE id= ? ";
     return this.db.executeSql(sql,[id]);
  }

Recibimos como parámetro id que es el id del sitio que queremos eliminar de la base de datos, ejecutamos la query y si todo ha ido bien e sitio se habrá eliminado para siempre de la base de datos.

Ahora que ya tenemos preparada la función para borrar el sitio en el provider vamos a volver al alert de confirmación en listado.ts para definir las operaciones a realizar al pulsar el botón “Si”, en la función handler del botón “Si” añadimos el siguiente código:

borrarSitio(id){

    let alert = this.alertCtrl.create({
      title: 'Confirmar borrado',
      message: '¿Estás seguro de que deseas eliminar este sitio?',
      buttons: [
        {
          text: 'No',
          role: 'cancel',
          handler: () => {
              // Ha respondido que no así que no hacemos nada         
           }
        },
        {
          text: 'Si',
          handler: () => {

              this.db.borrarSitio(id).then((res)=>{
            // Una vez borrado el sitio recargamos el listado
              this.db.getSitios().then((res)=>{
              this.sitios = [];
              for(var i = 0; i < res.rows.length; i++){
                  this.sitios.push({
                    id : res.rows.item(i).id,
                    lat: res.rows.item(i).lat,
                    lng: res.rows.item(i).lng,
                    address: res.rows.item(i).address,
                    description: res.rows.item(i).description,
                    foto: res.rows.item(i).foto
                  });
              }

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

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

    alert.present();

   }

Lo primero que hacemos es llamar al método que acabamos de crear en el provider con this.db.borrarSitio(id) pasándole como parámetro el id del sitio.

En el then lo que vamos ha hacer es refrescar el listado para ello debemos modificar la variable miembro this.sitios que contiene los sitios que se muestran en el listado, lo que hacemos es volver a cargar de la base de datos los sitios, como acabamos de borrar un sitio este no aparecerá. Para cargar los sitios de la base de datos utilizamos el método this.db.getSitios que ya tenemos definido y hacemos exactamente el mismo proceso que en la función  ionViewDidEnter para rellenar el array this.sitios con los datos obtenidos desde la base de datos. De hecho podemos definir una función que se encargue de recoger los sitios de la base de datos y rellenar el array this.sitios en lugar de repetir el código de ionViewDidEnter, eso ya lo dejo como mejora opcional.

Si ejecutáis el código comprobaréis que ya podéis borrar los sitio que no os interesen conservar en vuestro dispositivo.

Eso es todo por hoy.

 

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

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

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

 

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

Hola a todos,

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

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

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

...

export class ModalDetalleSitio {

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

...

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

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

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

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

<!--
  Generated template for the ModalDetalleSitio page.

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

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

</ion-header>


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

Vayamos por partes:

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

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

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

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

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

 <ion-card *ngIf="edit">

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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



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

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

...

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

editar(){
   this.edit = true;
 }

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

sacarFoto(){

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


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

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

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

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

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

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

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

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

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

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

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

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

/*
  Generated class for the DbProvider provider.

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

  db : SQLiteObject = null;

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

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

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

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

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

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

}

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

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

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

  sitio: any;
  edit : boolean = false;


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

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

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

  comoLlegar(){

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

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

 }

 editar(){
   this.edit = true;
 }

 sacarFoto(){

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


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

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


}

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

 

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

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

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

 

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

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

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

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

ionic g page modalDetalleSitio

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

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

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

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

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

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

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

<!--
  Generated template for the Listado page.

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

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

</ion-header>


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

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

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

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

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

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

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

...

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

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

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

...
export class ModalDetalleSitioPage {

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

...

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

<!--
  Generated template for the ModalDetalleSitio page.

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

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

</ion-header>


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  ]
})
export class AppModule {}

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

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

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

  sitio: any;

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

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

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

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

 }
}

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

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

Modal detalle del sitio
Modal detalle del sitio

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

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

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

 

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

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

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

 

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

Hola a todos:

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

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

Provider

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

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

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

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

ionic g provider db

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

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

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

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

/*
  Generated class for the Db provider.

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

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

}

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

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

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

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

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

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

/*
  Generated class for the Db provider.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

    });
  }
}

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

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



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

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

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

...

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

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

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

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

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

Si todo ha ido bien cerramos el modal.

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

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

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

/*
  Generated class for the Db provider.

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

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

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

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

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

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

}

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

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


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

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

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

...

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

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

Ciclo de vida de una página

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

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

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

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

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

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

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

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

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

Añadimos el método ionViewDidEnter al controlador de la página listado en el archivo listado.ts y hacemos la llamada para extraer nuestros sitios de la base de datos:

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


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

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

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

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

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

}

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

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

Ahora solo nos queda mostrarlos en el listado:

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

<!--
  Generated template for the Listado page.

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

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

</ion-header>

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

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

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

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

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

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

 

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

ionic cordova run android

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

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

 

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