Fábrica de Código

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!

Respostas de 10

  1. 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. 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. Olá professor,

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

    Obrigado.

  6. 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. 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!