Search code examples
typescriptionic-frameworkionic-storage

Ionic Storage : TypeError: Cannot read property 'get' of undefined


I'm using Ionic Storage to store data on the long run. Right now i wanna retrieve data when i go to the Statistics page, so i called the Service i made and wrote the methods in the ngOnInit of the statistics page but it doesn't recognize the instanciation of the storage class if i understood the issue correctly but the weird thing is that it does work when i write the methods in the ionViewWillEnter(). I made both ngOnInit and ionViewWillEnter async but i still get an error for the ngOnInit. I could go with the ionViewWillEnter trick if i'm not wrong it is similar to calling the methods in ngOnInit in my use case but i'm still curious of why it gets me errors...

TS of the statistics page :

import { StatsService } from './../service/stats.service';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-statistics',
  templateUrl: './statistics.page.html',
  styleUrls: ['./statistics.page.scss'],
})
export class StatisticsPage implements OnInit {

  testsAmount: any = 0;

  constructor(public statsService: StatsService) {}

  addJohn(){
    this.statsService.set("1", "john");
  }

  removeAll(){
    this.statsService.clearAll();
  }

  async getJohn(){
    console.log(await this.statsService.get("1"));
  }

  

  async ngOnInit() {
      await this.statsService.set("testSetngOnInit", "blabla");
      console.log("testSet initialized from the ngOnInit");
      console.log(await this.statsService.get("testSetngOnInit"));
  }

  async ionViewWillEnter(){
    await this.statsService.set("testSetionViewWillEnter", "blabla");
      console.log("testSet initialized from the ionViewWillEnter");
    console.log(await this.statsService.get("testSetionViewWillEnter"));
  }

  

}

TS of the Service :

import { Injectable } from '@angular/core';
import { Storage } from '@ionic/storage-angular';
import * as CordovaSQLiteDriver from 'localforage-cordovasqlitedriver';

@Injectable({
  providedIn: 'root'
})
export class StatsService {
  
  // private _storage: Storage | null = null;
  private _storage: Storage;

  constructor(public storage: Storage) { 
    this.init();
  }

  async init() {
    // If using, define drivers here: await this.storage.defineDriver(/*...*/);
    await this.storage.defineDriver(CordovaSQLiteDriver);
    const storage = await this.storage.create();
    this._storage = storage;
  }

  async keyExistence(key: string){
    if(await this.get(key) == null){
      return false;
    }
    else{
      return true;
    }
  }

  // Create and expose methods that users of this service can
  // call, for example:
  async set(key: string, value: any) {
    await this._storage?.set(key, value);
  }

  async clearAll() {
    await this._storage.clear();
  }

  async get(key: string) {
    return await this._storage.get(key);
  }

}

And this is what i get in the console : console

And my IndexDB :

IndexDB

Thanks in advance !


Solution

  • Alright, I found the answer ! This is actually very simple to solve, so stupid that I didn't think about it... So instead of instanciating the service in its own class, you have to instanciate it in the class where you use it, so in my case :

    What I had in the service constructor :

    constructor(public storage: Storage) { 
        this.init();
      }
    

    But the thing is the constructor cannot use the async statement so you can't tell your class to wait for it, so what you need is to init inside the ngOnInit of the class using it :

    My page using the service :

    async ngOnInit() {
          await this.statsService.init();
      }
    

    Hope this would help some people :)