Skip to content

Instantly share code, notes, and snippets.

@yclim95
Created May 3, 2018 13:12
Show Gist options
  • Save yclim95/dd2655cdb83ca5bc9cd27cd88f9ff8ad to your computer and use it in GitHub Desktop.
Save yclim95/dd2655cdb83ca5bc9cd27cd88f9ff8ad to your computer and use it in GitHub Desktop.
Ionic: Storing Data

1. Setting up Ionic Storage

Ionic Storage is our go to package for easily managing data. With Ionic Storage we can save JSON objects and key/value pairs to different storage engines, unified through one interface.

Ok in easy this means, Storage will internally select which storage engine is available and select the best possible solution for us. If you run the preview, it will try

  1. IndexedDB
  2. WebSQL & finally...
  3. localstorage

The problem with localstorage in general is that this can get cleaned from the OS of a mobile device and you loose all data. Not a very good idea.

To get the best soring solution on a device we now need to add a new Cordova plugin which will allow access to the SQLite storage which Ionic Storage will then use internally. Go ahead and run:

ionic cordova plugin add cordova-sqlite-storage --save

The Storage package is already bundled with our app, we don’t have to install something new. We only want to generate a new provider which will handle our connection to the storage, so also run:

ionic g provider favorite

Of course we now need to change a few things inside our src/app/app.module.ts as you might alread expect. We need to hook up our new provider and also add the IonicStorageModule to our module:

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 { HttpClientModule } from '@angular/common/http';
import { ApiProvider } from './../providers/api/api';
import { EmailComposer } from '@ionic-native/email-composer';
 
import { FavoriteProvider } from './../providers/favorite/favorite';
import { IonicStorageModule } from '@ionic/storage';
 
@NgModule({
  declarations: [
    MyApp
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    IonicModule.forRoot(MyApp),
    IonicStorageModule.forRoot()
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp
  ],
  providers: [
    StatusBar,
    SplashScreen,
    ApiProvider,
    FavoriteProvider,
    EmailComposer,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ]
})
export class AppModule {}

That’s it, we are now ready to use the Storage!

2. Working with Ionic Storage

Like in the HTTP lesson before it’s a good idea to maintain your logic in a provider and not directly inside the class of the view. We have created a FavoriteProvider, because we want to be able to start films which we like the most.

So we need to be able to store the IDs of those films, check if a current film is already a favorite and of course revert everything again.

With Ionic Storage we can either call set() or get() and both will return a Promise. Unlike our Observable from before this is only handled with a then() block, but the operation is still async! * If we want to get a list of all IDs we can easily return the Storage value for our predefined STORAGE_KEY.

To favorite a film, we need to retrieve the list of all films already inside the storage and then either add the film or set a completely new object if nothing is there yet.

Don’t worry about duplicates, we will handle this from the page later.

To remove a film we use more or less the same logic, but now we get the index of our film inside the array and call the splice method to remove it from the array before we save it back.

Open your src/providers/favorite/favorite.ts and replace it with:

import { Injectable } from '@angular/core';
import { Storage } from '@ionic/storage';
 
const STORAGE_KEY = 'favoriteFilms';
 
@Injectable()
export class FavoriteProvider {
 
  constructor(public storage: Storage) { }
 
  isFavorite(filmId) {
    return this.getAllFavoriteFilms().then(result => {
      return result && result.indexOf(filmId) !== -1;
    });
  }
 
  favoriteFilm(filmId) {
    return this.getAllFavoriteFilms().then(result => {
      if (result) {
        result.push(filmId);
        return this.storage.set(STORAGE_KEY, result);
      } else {
        return this.storage.set(STORAGE_KEY, [filmId]);
      }
    });
  }
 
  unfavoriteFilm(filmId) {
    return this.getAllFavoriteFilms().then(result => {
      if (result) {
        var index = result.indexOf(filmId);
        result.splice(index, 1);
        return this.storage.set(STORAGE_KEY, result);
      }
    });
  }
 
  getAllFavoriteFilms() {
    return this.storage.get(STORAGE_KEY);
  }
 
}

If we just want to check whether a film is already stored we retrieve the array and check if the key can be found inside.

Now we have already implemented all of our business logic for storing, it’s time to change our UI so we can actually use those functions!

3. Using our Favorite Provider

Whenever we enter our FilmDetailsPage we want to check whether the current film is already a favorite or not.

Because inside our view we will use a new syntax called *ngIf which allows us to test if a condition is true or not. By doing this we can add the code for 2 different buttons, but only one will be displayed at a time!

The condition will check for a variable inside our class, so start with the view and change your src/pages/film-details/film-details.html to:

<ion-header>
  <ion-navbar color="primary">
    <ion-title>{{ film.title }}</ion-title>
        <ion-buttons end>
      <button ion-button icon-only (click)="unfavoriteFilm()" *ngIf="isFavorite"><ion-icon name="star"></ion-icon></button>
      <button ion-button icon-only (click)="favoriteFilm()" *ngIf="!isFavorite"><ion-icon name="star-outline"></ion-icon></button>
    </ion-buttons>
  </ion-navbar>
</ion-header>
 
<ion-content padding>
  <ion-card>
    <ion-card-content>
      {{ film.opening_crawl }}
    </ion-card-content>
  </ion-card>
 
  <button ion-button full (click)="shareFilm()">Share by Email</button>
</ion-content>

So we got either an outline button or a full button, depending on the state of isFavorite. Inside the class we now have to take care of setting the value correctly.

But as we have implemented our provider so super clear and clean before, it’s absolutely no problem to build the page, right?

After we get the data for the current film we pass the ID to our provider and first of all check if that film is already one of our favorites. Inside the then() block we can use the result and set it to the variable for our condition.

To favorite or remove a favorite we can rely on the functions we implemented previously inside the provider, and inside the callback block we just change our local variable manually. We could also use a return value here but we want to keep things simple, and it should do the job for us!

So now go ahead and change your src/pages/film-details/film-details.ts to:

import { FavoriteProvider } from './../../providers/favorite/favorite';
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { EmailComposer } from '@ionic-native/email-composer';
 
@IonicPage()
@Component({
  selector: 'page-film-details',
  templateUrl: 'film-details.html',
})
export class FilmDetailsPage {
  film: any;
  isFavorite = false;
 
  constructor(public navCtrl: NavController, public favoriteProvider: FavoriteProvider, public navParams: NavParams, private emailComposer: EmailComposer) {
    this.film = this.navParams.get('film');
    this.favoriteProvider.isFavorite(this.film.episode_id).then(isFav => {
      this.isFavorite = isFav;
    })
  }
 
  favoriteFilm() {
    this.favoriteProvider.favoriteFilm(this.film.episode_id).then(() => {
      this.isFavorite = true;
    });
  }
 
  unfavoriteFilm() {
    this.favoriteProvider.unfavoriteFilm(this.film.episode_id).then(() => {
      this.isFavorite = false;
    });
  }
 
  shareFilm() {
    let email = {
      to: 'saimon@devdactic.com',
      subject: 'I love this one: ' + this.film.title,
      body: 'Can you remember the opening?<br><br>\"' + this.film.opening_crawl + '\"',
      isHtml: true
    };
 
    this.emailComposer.open(email);
  }

You can now navigate to the details pages and should be able to star and unstar a film!

Ionic Favourite Email Compose

Of course this is not only a tiny effect, it’s actually stored inside the database. You navigate to other movies, start them and you will see that your selecting will be stored all the time, even if your refresh the window!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment