Archivo de la etiqueta: iOS

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: