CRUD Ionic3 - Angular4 - SQLite With POST to WebService


#1

CRUD de Usuarios con SQLite, Ionic 3 y Angular 4

Validación de Formularios y Post en un WebService

Antes de iniciar, verificar que tener las siguientes herramientas disponibles:
- Node. Js
- Ionic 3 (Framework)
- Angular 4
- Cordova
- Visual Studio Code (Recomendado)
- Dispositivo Movil con la App Ionic View para pruebas.

Comandos básicos de Ionic y Git para codificación

Ionic3

ionic start blank: Crea e inicia la aplicación En blanco
ionic start tabs: Con tabs
ionic start sidemenu: Con menu de opciones a un costado
cd : Acceso a sub carpeta de la aplicación
ionic serve: Ejecuta servidor en navegador web
ionic serve --lab: Ejecuta servidor con vista de plataformas
ionic serve -c: Ejecuta servidor con detalle de errores
ionic cordova run browser: Ejecuta el servidor embebido de apache cordova (Recomendado)
ionic g page : Genera una nueva pagina con su ts – html – scss – module.ts

Para más información, visitar las páginas de cada tecnología y ver su documentación oficial.

Paso a paso

1. Descargar Node.js previamente donde se podrá realizar la instalación de herramientas y frameworks a través de npm console.

2. Descargar Visual Studio Code preferiblemente para el desarrollo de la aplicación y luego lo abrimos. Abrimos la consola de comandos con Ctrl + Ñ para instalar las herramientas y frameworks necesarios para el desarrollo.

3. Instalar Apache cordova con el comando: npm install -g cordova
Si se muestran alertas de Warning no hay problema desde que no muestre alertas de Error.

Para ver la versión de cordova instalado con el comando: cordova -v

4. Instalar Ionic Framework con el comando: npm install -g cordova ionic (La letra -g hace referencia a que la instalación es global y solo se tendrá que hacer una vez)

Para ver la versión de ionic instalado con el comando: ionic info

5. Crear una carpeta vacía en el directorio deseado y con el nombre del proyecto.

6. Arrastrar la carpeta creada a Visual Studio y abrir la consola de comandos con Ctrl + Ñ

7. Para crear el proyecto y dar inicio se hace con el comando:
ionic start blank Ó ionic start tabs (Con Vista precargada)

8. Responder que si a la integración de cordova con la aplicación para IOs y Android y también si a la instalación del SDK para conectar a la aplicacion. Si se tiene cuenta creada en Ionic ingresar las credenciales sino este paso se puede realizar mas adelante.

9. Ingresar a la raíz de la aplicación con el comando: cd el cual se definió en el ionic start

10. Ingresar el comando para visualizar la aplicación: ionic serve –lab o el deseado para previsualizar la aplicación en construcción.

Ya se visualiza los archivos creados en el proyecto en la parte izquierda.
Se abrirá la vista en el navegador con la ruta por defecto: localhost:8100

• Las vistas html se encuentran en src -> pages
Cada vista esta compuesta por su html, scss (estilos) y ts (typescript)
Si se desea agregar una nueva vista, el comando es: ionic generate page

• Para configuración del proyecto, se puede a través del archivo: config.xml

• La carpeta www es el código ya compilado y generado para el servidor.

• La carpeta assets es donde incluimos las imágenes, recursos e iconos de la aplicación.

• La carpeta app es donde hacemos todas las cosas incluyentes que se hacen a nivel general de la aplicación. Como importaciones, etc.

Construccion de la Aplicacion

Agregar el plugin SQLite con los siguientes comandos:

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

Abrir y agregar en ‘src/app/app.module.ts’ las siguientes importaciones:

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 { SQLite } from '@ionic-native/sqlite';
import { Toast } from '@ionic-native/toast';
import { MyApp } from './app.component';

import { HomePage } from '../pages/home/home';
import { AddDataPage } from '../pages/add-data/add-data';
import { EditDataPage } from '../pages/edit-data/edit-data';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

Agregarlos Tambien a @NgModule en ‘imports’:

imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
    FormsModule,
    ReactiveFormsModule,
    HttpModule,
  ],

Agregarlos Tambien a @NgModule en ‘ providers’ :

providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler},
    SQLite,
    Toast
  ]

Crear la lista de Usuarios, con Agregar, Editar y Eliminar

Abra ‘src / pages / home / home.ts’ y agregar las siguientes importaciones:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { SQLite, SQLiteObject } from '@ionic-native/sqlite';
import { AddDataPage } from '../add-data/add-data';
import { EditDataPage } from '../edit-data/edit-data';
import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptions } from '@angular/http';
import { Toast } from '@ionic-native/toast';
import 'rxjs/add/operator/toPromise';

Estas páginas aún no están creadas, las creamos posteriormente.

Ahora inyectar SQLite en el constructor:

constructor(public navCtrl: NavController,
    private sqlite: SQLite,
    private toast: Toast,
    public http: Http) {
  }

Agregar el arreglo de los usuarios:
contactos: any[] = [];

Agregar estas funciones debajo del constructor para el CRUD completo:

ionViewDidLoad() {
    this.getData();
  }

  ionViewWillEnter() {
    this.getData();
  }

  getData() {
    this.sqlite.create({
      name: 'ionicdb.db',
      location: 'default'
    }).then((db: SQLiteObject) => {
      
   db.executeSql('CREATE TABLE IF NOT EXISTS Contactos(id INTEGER PRIMARY KEY AUTOINCREMENT, nombre TEXT, telefono TEXT, ext TEXT, correo TEXT, empresa TEXT, CONSTRAINT nombre_unique UNIQUE(nombre), CONSTRAINT correo_unique UNIQUE(correo))', {})
        .then(res => console.log('Executed SQL'))
        .catch(e => {
          console.log(e);
          this.toast.show("Create:" + e.message, '5000', 'center').subscribe(
            toast => {
              console.log(toast);
            }
          );
        });
      db.executeSql('SELECT * FROM Contactos ORDER BY id DESC', {})
        .then(res => {
          this.contactos = [];
          for (var i = 0; i < res.rows.length; i++) {
            this.contactos.push({ id: res.rows.item(i).id, nombre: res.rows.item(i).nombre, telefono: res.rows.item(i).telefono, ext: res.rows.item(i).ext, correo: res.rows.item(i).correo, empresa: res.rows.item(i).empresa })
          }
        })
    })
  }

  addData() {
    this.navCtrl.push(AddDataPage);
  }

  editData(id) {
    this.navCtrl.push(EditDataPage, {
      id: id
    });
  }

  deleteData(id) {
    this.sqlite.create({
      name: 'ionicdb.db',
      location: 'default'
    }).then((db: SQLiteObject) => {
      db.executeSql('DELETE FROM Contactos WHERE id=?', [id])
        .then(res => {
          console.log(res);
          this.getData();
        })
        .catch(e => console.log(e));
    }).catch(e => console.log(e));
  }
	
_//Metodo para enviar datos por método POST al WebService (En dado caso que se necesite, sino no implementar)_

postRequest() {
    this.sqlite.create({
      name: 'ionicdb.db',
      location: 'default'
    }).then((db: SQLiteObject) => {
      db.executeSql('SELECT * FROM Contactos ORDER BY id DESC', {})
        .then(result => {
          this.contactos = [];
          for (var i = 0; i < result.rows.length; i++) {
            this.contactos.fill({
              id: result.rows.item(i).id,
              nombre: result.rows.item(i).nombre,
              telefono: result.rows.item(i).telefono,
              ext: result.rows.item(i).ext,
              correo: result.rows.item(i).correo,
              empresa: result.rows.item(i).empresa
            })

   var headers = new Headers();
            headers.append("Accept", 'application/json');
            headers.append('Content-Type', 'application/x-www-form-urlencoded');
            let options = new RequestOptions({ headers: headers });
            let params = "nombre=" + result.rows.item(i).nombre
             + "&telefono=" + result.rows.item(i).telefono
             + "&ext=" + result.rows.item(i).ext
             + "&correo=" + result.rows.item(i).correo
             + "&empresa=" + result.rows.item(i).empresa;


   this.http.post("url", params, options)
              .subscribe(contactos => {
                this.contactos.push(contactos.json())
                console.log(contactos['_body']);
                this.toast.show('Datos Sincronizados!', '4000', 'center').subscribe(
                  toast => {
                    console.log(contactos);
                  }
                )
              }, error => {
                console.log(error.message);// Error obteniendo los datos!
                this.toast.show('Error en la sincronizacion!' + error.message, '4000', 'center').subscribe(
                  toast => {
                    console.log(toast);
                  }
                )
              });
          }
          console.log('Result')
        })
        .catch(e => console.log(e));
      console.log('Create')
    })
      .catch(e => console.log(e));
  }

}

Cabe mencionar que en el WebService implementar la BD de la misma manera como se declara aca.

Abrir ‘src / pages / home / home.html’ y agregar:

<ion-header>
  <ion-navbar>
    <ion-title>
      Titulo App
    </ion-title>
    <ion-buttons end>
      <button ion-button icon-only (click)="addData()">
        <ion-icon name="add-circle"></ion-icon>
      </button>
    </ion-buttons>
  </ion-navbar>
</ion-header>


<ion-content padding>
  <h2 text-center>Lista de Contactos
  <ion-icon name="contacts"></ion-icon>
 </h2>
  <ion-list>
    <ion-item-sliding *ngFor="let contacto of contactos; let i=index">
      <ion-item nopadding>
          <h3>
              {{contacto.nombre}}
            </h3>
        <p>
          <span>{{contacto.telefono}}</span><br>
                {{contacto.ext}}<br>
                {{contacto.correo}}<br>
                {{contacto.empresa}}
        </p>
        
      </ion-item>
      <ion-item-options side="right">
        <button ion-button color="primary" (click)="editData(contacto.id)">
          <ion-icon name="create"></ion-icon>
        </button>
        <button ion-button color="danger" (click)="deleteData(contacto.id)">
          <ion-icon name="trash"></ion-icon>
        </button>
      </ion-item-options>
    </ion-item-sliding>
  </ion-list>
  
</ion-content>
<ion-footer>
  <ion-toolbar>//Boton del POST al WebService (Implementar si se necesita)
    <button ion-button type="submit" block (click)="postRequest()">Sincronizar Datos</button>
  </ion-toolbar>
</ion-footer>

El boton de Agregar se presenta en la parte superior de la vista y los botones de Agregar y Eliminar deslizando el ítem agregado hacia la izquierda.

Crear una nueva página para agregar nuevos datos.
ionic g page AddData
Con este comando se crea una nueva carpeta con su html – ts – scss – module.ts

Abra ‘src / pages / add-data / add-data.ts’ y reemplace el codigo:

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { SQLite, SQLiteObject } from '@ionic-native/sqlite';
import { Toast } from '@ionic-native/toast';
import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms';

@IonicPage()
@Component({
  selector: 'page-add-data',
  templateUrl: 'add-data.html',
})
export class AddDataPage  {

  data = { nombre:"", telefono:"", ext:"", correo:"", empresa:"" };

  registerForm: FormGroup;

  constructor(public navCtrl: NavController,
    public navParams: NavParams,
    private sqlite: SQLite,
    private toast: Toast,
    public formBuilder: FormBuilder) {
      this.registerForm = formBuilder.group({
        nombre: ['', Validators.compose([Validators.required])],
        telefono: ['', Validators.compose([Validators.required, Validators.minLength(7), Validators.maxLength(10)])],
        ext: ['', Validators.compose([Validators.required])],
        correo: ['', Validators.compose([Validators.required, Validators.pattern('^[^\s@]+@[^\s@]+\.[^\s@]{2,}$'), Validators.minLength(1)])],
        empresa: ['', Validators.compose([Validators.required])]
        
      });
    }

  saveData() {
    if (this.registerForm.valid){
    this.sqlite.create({
      name: 'ionicdb.db',
      location: 'default'
    }).then((db: SQLiteObject) => {
      db.executeSql('INSERT INTO Contactos (id, nombre, telefono, ext, correo, empresa) VALUES(NULL,?,?,?,?,?)',[this.data.nombre,this.data.telefono,this.data.ext,this.data.correo,this.data.empresa])
        .then(res => {
          console.log(res);
          this.toast.show('Contacto Registrado!', '4000', 'center').subscribe(
            toast => {
              this.navCtrl.popToRoot();
            }
          );
        })
        .catch(e => {
          console.log(e);
          this.toast.show("El correo o nombre ya existe!", '4000', 'center').subscribe(
            toast => {
              console.log(toast);
            }
          );
        });
    }).catch(e => {
      console.log(e);
      this.toast.show("Error!", '5000', 'center').subscribe(
        toast => {
          console.log(toast);
        }
      );
    });
  }
}
}

Luego abra ‘src / pages / add-data / add-data.html’ y reemplace el código:

<script src="https://code.angularjs.org/1.2.1/angular.min.js"></script>

<ion-header>
  <ion-navbar>
    <ion-title>Titulo App</ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>

  <h2 text-center>Agregar Contacto
      <ion-icon name="contact"></ion-icon>
  </h2>

  <form [formGroup]="registerForm" #formDir="ngForm" (ngSubmit)="saveData(registerForm.value)">



    <ion-item>
      <ion-label color="primary" floating>Nombre y Apellido: </ion-label>
      <ion-input type="text"  [(ngModel)]="data.nombre" (ngModelChange)="data.nombre = $event.toLocaleUpperCase()" name="nombre" formControlName="nombre" ></ion-input>
    </ion-item>
    
    <ion-item>
      <ion-label color="primary" floating>Telefono: </ion-label>
      <ion-input  type="tel"  [(ngModel)]="data.telefono" name="telefono" formControlName="telefono"></ion-input>
    </ion-item>
    
    <ion-item>
        <ion-label color="primary" floating>Ext: </ion-label>
        <ion-input type="tel"  [(ngModel)]="data.ext" name="ext" formControlName="ext" ></ion-input>
      </ion-item>

    <ion-item>
      <ion-label color="primary" floating>Correo: </ion-label>
      <ion-input  type="email"  [(ngModel)]="data.correo" (ngModelChange)="data.correo = $event.toLocaleUpperCase()" name="correo" formControlName="correo"></ion-input>
    </ion-item>
    
    <ion-item>
      <ion-label color="primary" floating>Empresa: </ion-label>
      <ion-input type="text"  [(ngModel)]="data.empresa" (ngModelChange)="data.empresa = $event.toLocaleUpperCase()" name="empresa" formControlName="empresa" ></ion-input>
    </ion-item>

    <button ion-button [disabled]="!registerForm.valid" type="submit" block>Guardar Contacto</button>
  </form>
</ion-content>

Crear una nueva página para editar los datos.
ionic g page EditData

Abra ‘src / pages / edit-data / edit-data.ts’ y reemplace el codigo:

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { SQLite, SQLiteObject } from '@ionic-native/sqlite';
import { Toast } from '@ionic-native/toast';

@IonicPage()
@Component({
  selector: 'page-edit-data',
  templateUrl: 'edit-data.html',
})
export class EditDataPage {

  data = { id:0, nombre:"", telefono:"", ext:"", correo:"", empresa:"" };

  constructor(public navCtrl: NavController,
    public navParams: NavParams,
    private sqlite: SQLite,
    private toast: Toast) {
      this.getCurrentData(navParams.get("id"));
  }

  getCurrentData(id) {
    this.sqlite.create({
      name: 'ionicdb.db',
      location: 'default'
    }).then((db: SQLiteObject) => {
      db.executeSql('SELECT id, nombre, telefono, ext, correo, empresa FROM Contactos WHERE id=?', [id])
        .then(res => {
          if(res.rows.length > 0) {
            this.data.id = res.rows.item(0).id;
            this.data.nombre = res.rows.item(0).nombre;
            this.data.telefono = res.rows.item(0).telefono;
            this.data.ext = res.rows.item(0).ext;
            this.data.correo = res.rows.item(0).correo;
            this.data.empresa = res.rows.item(0).empresa;
          }
        })
        .catch(e => {
          console.log(e);
          this.toast.show("Error al ver contactos!", '5000', 'center').subscribe(
            toast => {
              console.log(toast);
            }
          );
        });
    }).catch(e => {
      console.log(e);
      this.toast.show("Error!", '5000', 'center').subscribe(
        toast => {
          console.log(toast);
        }
      );
    });
  }

  updateData() {
    this.sqlite.create({
      name: 'ionicdb.db',
      location: 'default'
    }).then((db: SQLiteObject) => {
      db.executeSql('UPDATE Contactos SET nombre=?,telefono=?,ext=?,correo=?,empresa=? WHERE id=?',[this.data.nombre,this.data.telefono,this.data.ext,this.data.correo,this.data.empresa,this.data.id])
        .then(res => {
          console.log(res);
          this.toast.show('Contacto Actualizado!', '4000', 'center').subscribe(
            toast => {
              this.navCtrl.popToRoot();
            }
          );
        })
        .catch(e => {
          console.log(e);
          this.toast.show("Error al editar!", '5000', 'center').subscribe(
            toast => {
              console.log(toast);
            }
          );
        });
    }).catch(e => {
      console.log(e);
      this.toast.show("Error!", '5000', 'center').subscribe(
        toast => {
          console.log(toast);
        }
      );
    });
  }

}

A continuación, abra y reemplace el código en ‘src / pages / edit-data / edit-data.html’

<ion-header>
  <ion-navbar>
    <ion-title>Titulo App</ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  <h2 text-center>Editar Contacto
      <ion-icon name="create"></ion-icon>
  </h2>

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

    <ion-item>
      <ion-label color="primary" floating>Nombre y Apellido: </ion-label>
      <ion-input type="text"  [(ngModel)]="data.nombre" (ngModelChange)="data.nombre = $event.toLocaleUpperCase()" name="nombre" ></ion-input>
    </ion-item>
    <ion-item>
      <ion-label color="primary" floating>Telefono: </ion-label>
      <ion-input type="tel"  [(ngModel)]="data.telefono" name="telefono"  ></ion-input>
    </ion-item>
    <ion-item>
        <ion-label color="primary" floating>Ext: </ion-label>
        <ion-input type="tel"  [(ngModel)]="data.ext" name="ext"  ></ion-input>
      </ion-item>
    <ion-item>
      <ion-label color="primary" floating>Correo: </ion-label>
      <ion-input type="email"  [(ngModel)]="data.correo" (ngModelChange)="data.correo = $event.toLocaleUpperCase()" name="correo" ></ion-input>
    </ion-item>
    <ion-item>
      <ion-label color="primary" floating>Empresa: </ion-label>
      <ion-input type="text"  [(ngModel)]="data.empresa" (ngModelChange)="data.empresa = $event.toLocaleUpperCase()" name="empresa"  ></ion-input>
    </ion-item>
    <ion-input type="hidden" [(ngModel)]="data.id" name="id"></ion-input>
    <button ion-button type="submit" block>Actualizar Contacto</button>
  </form>
</ion-content>

Antes de ejecutar la aplicación, primero elimine y agregue las plataformas con los siguientes comandos en consola:


ionic cordova platform rm android
ionic cordova platform rm ios
ionic cordova platform add android
ionic cordova platform add ios

NOTAS
SQLite no se permite ejecutar en navegador, solamente en Ionic View (App Movil).

EL método de postRequest(), se envían los parámetros del arreglo como un Objeto no como un JSON.

Éxitos!
Sergio Camargo – zergiodkamargo.sc@gmail.com


#2

Hola @zergiok

Gracias por la guía, pero al momento de correr ionic cordova platform add android me da el siguiente error…

Error during processing of action! Attempting to revert…
Failed to install ‘cordova-sqlite-storage’: CordovaError: Uh oh!
“/home/desarrollo/Escritorio/otrapp/plugins/cordova-sqlite-storage/node_modules/cordova-sqlite-storage-dependencies/libs/sqlite-connector.jar” not found!

Tal vez me puedes ayudar, e probado varias soluciones y continua el problema


#3

Hey!
Tiene que usar estos comandos para instalar SQLite y prestar atención a que SQLIte solo se ejecute en dispositivos, no en el navegador…

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

Espero que te ayude…


#4

Saludos @TomCosta !!

Gracias por contestarme, esos dos comandos me corren bien el problema se presenta cuando corro:

cordova platform add android

me arroja este error

Failed to install ‘cordova-sqlite-storage’: CordovaError: Uh oh!
"/home/desarrollo/Escritorio/otrapp/plugins/cordova-sqlite-storage/node_modules/cordova-sqlite-storage-dependencies/libs/sqlite-connector.jar" not found!