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: