Menu

SQLite + Ionic 2 en 6 pasos

nativedemosionic2 - November 28, 2016 por Nicolas Molina

Anteriormente hemos hablado sobre firebase, pouch y Rest API para el consumo de datos, ahora en este nuevo demo haremos la integración con SQLite que es una base de datos nativa que proveen los dispositivos móviles.

## Paso 1: Iniciando el proyecto

Lo primero que haremos será iniciar un nuevo proyecto con ionic, si no lo recuerdas puedes ver esto con mas detalle en la Introduccion a Ionic 2. Vamos a nuestra terminal y ejecutamos:

ionic start demo107 blank --v2

Ahora entramos a la carpeta del proyecto desde nuestra terminal con:

cd demo107

Como iniciamos nuestro proyecto con el template blank tendremos una estructura básica del proyecto, la carpeta en la que vamos a trabajar sera app.

Paso 2: Instalar SQLite al proyecto

Ahora agregamos el plugin en el proyecto:

ionic plugin add cordova-sqlite-storage --save

La documentación en ionic native la puedes ver aquí y la doc del plugin de cordova aquí.

Paso 3: Crear un servicio

Vamos a crear el servicio TasksService que se encargará de manejar todos los datos, usaremos ionic generator para crear este servicio:

ionic g provider tasks-service

En el servicio vamos a importar a SQLite desde ionic-native (línea 2) y luego creamos declaramos la variable db (línea 10) y en el constructor creamos una instancia de SQLite.

1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@angular/core';
import { SQLite } from 'ionic-native';

@Injectable()
export class TasksService {

  db: SQLite = null;

  constructor() {
    this.db = new SQLite();
  }

}

Seguido a esto vamos a crear el método openDatabase que se encargada de abrir la base de datos y nos retorna una promesa:

openDatabase(){
  return this.db.openDatabase({
    name: 'data.db',
    location: 'default' // the location field is required
  });
}

Ahora tendremos el método createTable que se encargara de crear la estructura de la base de datos que queremos. En este caso haremos una tabla para gestionar tareas.

createTable(){
  let sql = 'CREATE TABLE IF NOT EXISTS tasks(id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, completed INTEGER)';
  return this.db.executeSql(sql, []);
}

Vamos a crear nuestro primer método funcional getAll el cual nos hará una consulta a la base de datos y obtiene todas las tareas que estén en la tabla y luego serán retornadas en una promesa.

getAll(){
  let sql = 'SELECT * FROM tasks';
  return this.db.executeSql(sql, [])
  .then(response => {
    let tasks = [];
    for (let index = 0; index < response.rows.length; index++) {
      tasks.push( response.rows.item(index) );
    }
    return Promise.resolve( tasks );
  })
}

Ahora con el método create vamos a ejecutar un INSERT para guardar una nueva tarea en la base de datos.

create(task: any){
  let sql = 'INSERT INTO tasks(title, completed) VALUES(?,?)';
  return this.db.executeSql(sql, [task.title, task.completed]);
}

Con el método update vamos a ejecutar un UPDATE para actualizar una tarea en la base de datos.

update(task: any){
  let sql = 'UPDATE tasks SET title=?, completed=? WHERE id=?';
  return this.db.executeSql(sql, [task.title, task.completed, task.id]);
}

Finalmente con el método delete vamos a ejecutar un DELETE para eliminar una tarea en la base de datos.

delete(task: any){
  let sql = 'DELETE FROM tasks WHERE id=?';
  return this.db.executeSql(sql, [task.id]);
}

En resumen todo nuestro servicio quedará así:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import { Injectable } from '@angular/core';
import { SQLite } from 'ionic-native';

@Injectable()
export class TasksService {

  // public properties

  db: SQLite = null;

  constructor() {
    this.db = new SQLite();
  }

  // public methods

  create(task: any){
    let sql = 'INSERT INTO tasks(title, completed) VALUES(?,?)';
    return this.db.executeSql(sql, [task.title, task.completed]);
  }

  createTable(){
    let sql = 'CREATE TABLE IF NOT EXISTS tasks(id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, completed INTEGER)';
    return this.db.executeSql(sql, []);
  }

  delete(task: any){
    let sql = 'DELETE FROM tasks WHERE id=?';
    return this.db.executeSql(sql, [task.id]);
  }

  getAll(){
    let sql = 'SELECT * FROM tasks';
    return this.db.executeSql(sql, [])
    .then(response => {
      let tasks = [];
      for (let index = 0; index < response.rows.length; index++) {
        tasks.push( response.rows.item(index) );
      }
      return Promise.resolve( tasks );
    })
  }

  openDatabase(){
    return this.db.openDatabase({
      name: 'data.db',
      location: 'default' // the location field is required
    });
  }

  update(task: any){
    let sql = 'UPDATE tasks SET title=?, completed=? WHERE id=?';
    return this.db.executeSql(sql, [task.title, task.completed, task.id]);
  }

}

Paso 4: El controlador

Ahora desde el controlador de la página home.ts vamos a implementar el servicio de TaskService en este controlador haremos uso de AlertController para crear tareas a partir de una Alert. Y creamos el arreglo tasks como vacío en línea 12.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { Component } from '@angular/core';
import { NavController, AlertController } from 'ionic-angular';

import { TasksService } from '../../providers/tasks-service';

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

  tasks: any[] = [];

  constructor(
    public alertCtrl: AlertController,
    public navCtrl: NavController,
    public tasksService: TasksService
  ) {}
}

Con el método getAllTasks vamos a obtener todas las tareas que estén en la base de datos y en la línea 4 asignamos la respuesta al array de tasks.

1
2
3
4
5
6
getAllTasks(){
  this.tasksService.getAll()
  .then(tasks => {
    this.tasks = tasks;
  })
}

Para crear una nueva tarea creamos un alert con un input que capture el título de la nueva tarea luego desde la línea 21 hasta la línea 28 creamos la tarea haciendo uso de tasksService y la agregamos al array tasks.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
openAlertNewTask(){
  let alert = this.alertCtrl.create({
    title: 'Crear tarea',
    message: 'escribe el nombre de la tarea',
    inputs: [
      {
        name: 'title',
        placeholder: 'Digitar nueva tarea.',
      }
    ],
    buttons: [
      {
        text: 'Cancelar',
        handler: () =>{
          console.log('cancelar');
        }
      },
      {
        text: 'Crear',
        handler: (data)=>{ 
          data.completed = false;
          this.tasksService.create(data)
          .then(response => {
            this.tasks.unshift( data );
          })
          .catch( error => {
            console.error( error );
          })
        }
      }
    ]
  });
  alert.present();
}

Con el método updateTask actualizamos una tarea pero este método recibe dos parámetros el primero la tarea que vamos a actualizar y el segundo será la posición exacta en el array tasks, estos parámetros serán enviados desde el template.

1
2
3
4
5
6
7
8
9
10
11
updateTask(task, index){
  task = Object.assign({}, task);
  task.completed = !task.completed;
  this.tasksService.update(task)
  .then( response => {
    this.tasks[index] = task;
  })
  .catch( error => {
    console.error( error );
  })
}

Con el método deleteTask eliminamos una tarea y recibe los dos mismos parámetros que el método updateTask y cuando sea eliminada removemos la tarea del array tasks con un splice (línea 5)

1
2
3
4
5
6
7
8
9
10
deleteTask(task: any, index){
  this.tasksService.delete(task)
  .then(response => {
    console.log( response );
    this.tasks.splice(index, 1);
  })
  .catch( error => {
    console.error( error );
  })
}

Finalmente el controlador de HomePage será así, observen que el método getAllTasks lo llamamos desde ionViewDidLoad (línea 21):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import { Component } from '@angular/core';
import { NavController, AlertController } from 'ionic-angular';

import { TasksService } from '../../providers/tasks-service';

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

  tasks: any[] = [];

  constructor(
    public alertCtrl: AlertController,
    public navCtrl: NavController,
    public tasksService: TasksService
  ) {}

  ionViewDidLoad(){
    this.getAllTasks();
  }

  deleteTask(task: any, index){
    this.tasksService.delete(task)
    .then(response => {
      console.log( response );
      this.tasks.splice(index, 1);
    })
    .catch( error => {
      console.error( error );
    })
  }

  getAllTasks(){
    this.tasksService.getAll()
    .then(tasks => {
      console.log(tasks);
      this.tasks = tasks;
    })
  }

  openAlertNewTask(){
    let alert = this.alertCtrl.create({
      title: 'Crear tarea',
      message: 'escribe el nombre de la tarea',
      inputs: [
        {
          name: 'title',
          placeholder: 'Digitar nueva tarea.',
        }
      ],
      buttons: [
        {
          text: 'Cancelar',
          handler: () =>{
            console.log('cancelar');
          }
        },
        {
          text: 'Crear',
          handler: (data)=>{ 
            data.completed = false;
            this.tasksService.create(data)
            .then(response => {
              this.tasks.unshift( data );
            })
            .catch( error => {
              console.error( error );
            })
          }
        }
      ]
    });
    alert.present();
  }

  updateTask(task, index){
    task = Object.assign({}, task);
    task.completed = !task.completed;
    this.tasksService.update(task)
    .then( response => {
      this.tasks[index] = task;
    })
    .catch( error => {
      console.error( error );
    })
  }

}

Paso 5: El template

En el template se encargará de llamar las funciones creadas en HomePage y mostrar las tareas de la base de datos.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<ion-header>
  <ion-navbar color="primary">
    <ion-title>Tasks</ion-title>
    <ion-buttons right>
      <button ion-button icon-only (click)="openAlertNewTask()">
        <ion-icon name="add"></ion-icon>
      </button>
    </ion-buttons>
  </ion-navbar>
</ion-header>

<ion-content>
   <ion-list>
     <ion-item *ngIf="tasks.length == 0"> 
       Empty
     </ion-item>
    <ion-item-sliding *ngFor="let task of tasks; let i = index">
      <ion-item >
        <ion-label>{{ task.title }}</ion-label>
        <ion-checkbox (ionChange)="updateTask( task, i )" [checked]="task.completed"></ion-checkbox>
      </ion-item>
      <ion-item-options side="right" icon-left>
        <button ion-button color="danger" (click)="deleteTask(task, i)">
          <ion-icon name="trash"></ion-icon>
          Delete
        </button>
      </ion-item-options>
    </ion-item-sliding>
  </ion-list>
</ion-content>

Paso 6: Añadir Servicio y llamar a openDatabase

En este último paso asegurate de que el servicio TasksService este agregado en el array de providers de app.module.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { NgModule, ErrorHandler } from '@angular/core';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { TasksService } from '../providers/tasks-service';

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

Y cuando la app esté lista para iniciar en app.component.ts asegurate de llamar a openDatabase y crear la tabla.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar, Splashscreen } from 'ionic-native';

import { HomePage } from '../pages/home/home';
import { TasksService } from '../providers/tasks-service';


@Component({
  template: `<ion-nav [root]="rootPage"></ion-nav>`
})
export class MyApp {
  rootPage: any = null;

  constructor(
    public platform: Platform,
    public tasksService: TasksService
  ) {
    this.platform.ready().then(() => {
      StatusBar.styleDefault();
      Splashscreen.hide();
      tasksService.openDatabase()
      .then(() => this.tasksService.createTable())
      .then(()=>{
        this.rootPage = HomePage;
      })
    });
  }
}

Resultado:


Nota: Este demo solo funciona desde el dispositivo o emulador ya que SQLite no está disponible desde la web.

Ver demo

Recuerda:

Si quieres ejecutar este demo un tu computadora, debes tener el entorno de ionic instalado y luego de descargar o clonar el proyecto debes ubicarte en tu proyecto en la terminal y luego ejecutar

npm install

Y luego

ionic state restore

esto es para descargar todas las dependencias del proyecto.


¿Quieres saber más? Suscríbete a nuestro Newsletter o únete a nuestro canal en Slack y mantente atento. Espero sea de utilidad y sigan programando :)

Por: Nicolas Molina