Como armazenar dados offline com Ionic Storage

Nessa vídeo aula eu mostro como armazenar dados offline com o Ionic Storage.

 

O que é o Ionic Storage

O Ionic Storage é uma maneira simples de armazenar dados no formato chave/valor seja em um PWA (Progressive Web App) ou em um aplicativo nativo.

No aplicativo nativo será priorizado o uso do SQLite, para isso é necessário instalar o plugin cordova-sqlite-storage, e em um PWA a ordem de utilização será IndexedDB, WebSQL, e localstorage.

ionic cordova plugin add cordova-sqlite-storage

 

É possível escolher a ordem de prioridade de utilização de armazenamento. Para isso no arquivo app.module.ts, na propriedade imports basta colocar conforme exemplo abaixo.

import { IonicStorageModule } from '@ionic/storage';

@NgModule({
  declarations: [...],
  imports: [
    IonicStorageModule.forRoot({
      name: '__mydb',
         driverOrder: ['indexeddb', 'sqlite', 'websql']
    })
  ],
  bootstrap: [...],
  entryComponents: [...],
   providers: [...]
})
export class AppModule { }

 

Criando uma aplicação de exemplo

No vídeo acima e mostro como criar uma app para salvar contatos utilizando o Ionic Storage.

O passo a passo abaixo é o mesmo mostrado no vídeo.

  • Passo 1: Criar o aplicativo.
  • Passo 2: Instalar o Ionic Storage.
  • Passo 3: Configurar o Ionic Storage.
  • Passo 4: Criar o provider para fazer o CRUD.
  • Passo 5: Criar a pagina de inclusão/alteração de contatos.
  • Passo 6: Alterar a pagina Home para listar e excluir os contatos.

 

Passo 1: Criar o aplicativo

ionic start NOME_DO_APP blank

 

Passo 2: Instalar o Ionic Storage

npm install --save @ionic/storage

 

Passo 3: Configurar o Ionic Storage

A configuração do Ionic Storage fica no arquivo 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 { IonicStorageModule } from '@ionic/storage';
import { DatePipe } from '@angular/common';
import { ContactProvider } from '../providers/contact/contact';

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

 

Passo 4: Criar o provider para fazer o CRUD

ionic g provider contact

 

Arquivo contact.ts

import { Injectable } from '@angular/core';
import 'rxjs/add/operator/map';
import { Storage } from '@ionic/storage';
import { DatePipe } from '@angular/common';

@Injectable()
export class ContactProvider {

  constructor(private storage: Storage, private datepipe: DatePipe) { }

  public insert(contact: Contact) {
    let key = this.datepipe.transform(new Date(), "ddMMyyyyHHmmss");
    return this.save(key, contact);
  }

  public update(key: string, contact: Contact) {
    return this.save(key, contact);
  }

  private save(key: string, contact: Contact) {
    return this.storage.set(key, contact);
  }

  public remove(key: string) {
    return this.storage.remove(key);
  }

  public getAll() {

    let contacts: ContactList[] = [];

    return this.storage.forEach((value: Contact, key: string, iterationNumber: Number) => {
      let contact = new ContactList();
      contact.key = key;
      contact.contact = value;
      contacts.push(contact);
    })
      .then(() => {
        return Promise.resolve(contacts);
      })
      .catch((error) => {
        return Promise.reject(error);
      });
  }
}

export class Contact {
  name: string;
  phone: number;
  birth: Date;
  active: boolean;
}

export class ContactList {
  key: string;
  contact: Contact;
}

 

Passo 5: Criar a pagina de inclusão/alteração de contatos

ionic g page edit-contact

 

Arquivo edit-contact.ts

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams, ToastController } from 'ionic-angular';
import { ContactProvider, Contact } from '../../providers/contact/contact';

@IonicPage()
@Component({
  selector: 'page-edit-contact',
  templateUrl: 'edit-contact.html',
})
export class EditContactPage {
  model: Contact;
  key: string;

  constructor(public navCtrl: NavController, public navParams: NavParams, private contactProvider: ContactProvider, private toast: ToastController) {
    if (this.navParams.data.contact && this.navParams.data.key) {
      this.model = this.navParams.data.contact;
      this.key =  this.navParams.data.key;
    } else {
      this.model = new Contact();
    }
  }

  save() {
    this.saveContact()
      .then(() => {
        this.toast.create({ message: 'Contato salvo.', duration: 3000, position: 'botton' }).present();
        this.navCtrl.pop();
      })
      .catch(() => {
        this.toast.create({ message: 'Erro ao salvar o contato.', duration: 3000, position: 'botton' }).present();
      });
  }

  private saveContact() {
    if (this.key) {
      return this.contactProvider.update(this.key, this.model);
    } else {
      return this.contactProvider.insert(this.model);
    }
  }

}

 

Arquivo edit-contact.html

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Storage Example
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>

  <ion-list>

    <ion-item>
      <ion-label stacked>Nome</ion-label>
      <ion-input type="text" name="name" [(ngModel)]="model.name"></ion-input>
    </ion-item>

    <ion-item>
      <ion-label stacked>Telefone</ion-label>
      <ion-input type="tel" name="phone" [(ngModel)]="model.phone"></ion-input>
    </ion-item>

    <ion-item>
      <ion-label stacked>Nascimento</ion-label>
      <ion-datetime displayFormat="DD/MM/YYYY" name="birth" [(ngModel)]="model.birth"></ion-datetime>
    </ion-item>

    <ion-item>
      <ion-label>Ativo</ion-label>
      <ion-checkbox name="active" [(ngModel)]="model.active"></ion-checkbox>
    </ion-item>

  </ion-list>

  <button ion-button block (click)="save()">Salvar</button>

</ion-content>

 

Passo 6: Alterar a pagina Home para listar e excluir os contatos

Arquivo home.ts

import { Component } from '@angular/core';
import { NavController, ToastController } from 'ionic-angular';
import { ContactProvider, Contact, ContactList } from '../../providers/contact/contact';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  contacts: ContactList[];

  constructor(public navCtrl: NavController, private contactProvider: ContactProvider, private toast: ToastController) { }

  ionViewDidEnter() {
    this.contactProvider.getAll()
      .then((result) => {
        this.contacts = result;
      });
  }

  addContact() {
    this.navCtrl.push('EditContactPage');
  }

  editContact(item: ContactList) {
    this.navCtrl.push('EditContactPage', { key: item.key, contact: item.contact });
  }

  removeContact(item: ContactList) {
    this.contactProvider.remove(item.key)
      .then(() => {
        // Removendo do array de items
        var index = this.contacts.indexOf(item);
        this.contacts.splice(index, 1);
        this.toast.create({ message: 'Contato removido.', duration: 3000, position: 'botton' }).present();
      })
  }

}

 

Arquivo home.html

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Storage Example
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  <ion-list>
    <ion-card *ngFor="let item of contacts" [ngClass]="{'item-inactive': !item.contact.active }">

      <ion-card-header>
        <h1>{{ item.contact.name }}</h1>
      </ion-card-header>

      <ion-card-content>
        <p>{{ item.contact.phone }} - {{ item.contact.birth | date:'dd/MM/yyyy' }} </p>
      </ion-card-content>

      <ion-row text-center>
        <ion-col>
          <button ion-button icon-left clear small (click)="editContact(item);">
            <ion-icon name="create"></ion-icon>
            <div>Editar</div>
          </button>
        </ion-col>
        <ion-col>
          <button ion-button icon-left clear small (click)="removeContact(item);">
            <ion-icon name="trash"></ion-icon>
            <div>Excluir</div>
          </button>
        </ion-col>
        <ion-col>
          <a href="tel:{{ item.contact.phone }}" ion-button icon-left clear small>
            <ion-icon name="call"></ion-icon>
            <div>Ligar</div>
          </a>
        </ion-col>
      </ion-row>

    </ion-card>
  </ion-list>

  <ion-fab right bottom>
    <button ion-fab color="primary" (click)="addContact()"><ion-icon name="add"></ion-icon></button>
  </ion-fab>
</ion-content>

 

Clique no botão abaixo para ver o código fonte gerado nessa aula

 

[button style=”btn-primary btn-lg” type=”link” target=”true” title=”Código fonte gerado na aula” link=”https://github.com/fabricadecodigo/IonicStorageExample” linkrel=””]

 

Referências

 

Gostou desse artigo? Aproveite e curta e compartilhe para que mais pessoas possam também visualiza-lo!

Ainda ficou alguma dúvida ou tem alguma sugestão? Deixa aí nos comentários!

10 comentários em “Como armazenar dados offline com Ionic Storage”

  1. Itamar Rocha

    Boa tarde, Felipe

    Sou eu novamente, estou com uma dúvida quanto a armazenamento de geolocalização ionic offline.

    Tem alguma coisa que me recomenda?

    Desde já agradeço parceiro

  2. Fala Itamar, depende do que você quer armazenar e o que você vai fazer com a informação.
    Mas você pode usar o Ionic Storage ou o próprio SQLite.

    Valeu!

  3. Usley Do Carmo

    Boa noite! Porque se eu altero o nome do campo exemplo ( na classe export class Contact eu altero o nome da ‘name: string’ para ‘nome: string’ ou acrescento mais um objeto da este erro:
    Uncaught( inpromise) navigation stack needs at least one root page?

  4. Fala Usley, blz?

    Essa alteração que você fez não bate com a mensagem de erro.
    Essa mensagem de erro diz que está faltando a rootPage.

    La no app.component tem uma variavel para definir qual é a pagina que deve ser aberta por padrão.
    Da uma olhada lá.

    Valeu

  5. Murilo Lourenço Cabral

    Olá professor,

    Essa é uma opção para salvar realtime database do firebase? Tem como me explicar o caminho que tenho que salvar?

    Obrigado.

  6. Matteus Sousa Buttowski

    Olá prof. Parabéns pelo tutorial. Tenho um pedido, pois tentei de várias formas e não consegui, podeira fazer um exemplo de salvar os dados de uma api no sqlite pra uso offline caso o usuário nao tenha conexao com internet?

  7. Murilo Lourenço Cabral

    Desculpe não fui claro…
    Gostaria de deixar os dados disponíveis offline persistente no dispositivo.
    Aguardo Obrigado por sua resposta.

  8. Com a SDK do Firebase para web, que é a usada no Ionic, não funciona muito bem a parte offline.
    O ideal nesse caso seria ter uma API rest e salvar os dados no SQLite.

    Valeu!

Comentários encerrados.