简体   繁体   中英

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: 安慰

And my IndexDB:

索引数据库

Thanks in advance !

The error message indicates that line 44 in stats.service.ts is the line which caused the issue. Your component ngOnInit is indeed accessing the service instance, but the service itself is failing. I imagine this is because your constructor for StatsService is calling this.init(); , but the constructor is not awaiting it (and I don't think it can). Thus when you try to access it that early (in ngOnInit) this._storage has not yet been initialized.

The reason statsService.set appears to succeed is because that method calls this._storage?.set(key, value) - note the ? which is a null conditional, meaning if _storage is null then the whole expression evaluates as null .

I don't know for sure, but I assume ionViewWillEnter is called later in the component lifecycle, and the stats service constructor has had enough time to fully initialize. On the other hand, ngOnInit is called right after the component constructor.

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:)

Call init in your AppComponent to create an instance of storage. and this should called only one time.

export class AppComponent implements OnInit,{

  constructor(
    private storage: StorageService
    ) {}

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM