Error in Ionic Storage: Unable to access property 'get' because it is undefined

Currently, I am utilizing the Ionic Storage framework to persist data for long durations. My objective is to retrieve data upon navigating to the Statistics page. However, I encountered an issue where the instantiation of the storage class is not recognized when I call the service methods within the ngOnInit of the statistics page. Strangely enough, the methods work as intended when placed within the ionViewWillEnter() function. Both ngOnInit and ionViewWillEnter are marked as async, yet the error persists for ngOnInit. While I could resort to using the ionViewWillEnter workaround which seems to achieve a similar outcome in my scenario, I am intrigued by the underlying reason for these errors...

Here is the TypeScript code for 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"));
  }

  

}

Here is the TypeScript code for 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);
  }

}

The following output is displayed in the console : https://i.sstatic.net/jD4D7.png

Here is my IndexDB data : https://i.sstatic.net/eUSzN.png

Thank you in advance!

Answer №1

Well, well, well! I've finally cracked the code! It's actually quite simple once you understand it, so simple that I can't believe I didn't figure it out sooner... Instead of creating an instance of the service in its own class, you need to create it in the class where you'll be using it. For example:

Previously, my service constructor looked like this:

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

But the issue is that the constructor can't handle async operations, so you can't make your class wait for it. What you need to do is call the init function inside the ngOnInit of the class that uses the service:

Here's how my page using the service looks now:

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

Hopefully, this tip will be helpful to some of you :)

Answer №2

The issue is pinpointed to line 44 in stats.service.ts as the root cause of the error message. While your component's ngOnInit is attempting to access the service instance, it seems that the service itself is encountering a failure. It is likely that the problem arises from the constructor in StatsService calling this.init(); and not awaiting its completion (which may not be possible). Consequently, when attempting to access it in ngOnInit, this._storage may not have been initialized yet.

The apparent success of statsService.set is due to the method utilizing this._storage?.set(key, value), with the null conditional ? ensuring that the expression evaluates to null if _storage is null.

It is probable that ionViewWillEnter is triggered later in the component lifecycle, allowing sufficient time for the stats service constructor to complete initialization. Conversely, ngOnInit is invoked immediately after the component constructor.

Answer №3

Make sure to call the init function in your AppComponent to initialize the storage instance, and remember that this should only be done once.

export class AppComponent implements OnInit,{

  constructor(
    private storage: StorageService
    ) {}

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Unable to modify the Express Request User type, however, I have the ability to incorporate new attributes to Request object

Encountering a familiar issue with what appears to be a simple fix. The Express Request object includes a user property that is specified as Express.User (an empty object). Attempting the common approach to redefining it: // index.d.ts import { User as P ...

...additional properties in React function components using TypeScript

Here is a snippet of code that I am working with: <InputComponent id="email" name={formik.values.email} type="text" formik={formik} className="signInInput" disabled/> However, there seems to be an issue with the disable ...

The Typescript error occurs when trying to assign a 'string' type to a 'SetStateAction<null>'

For my project, I am delving into creating a global context using TypeScript. As a newcomer to TypeScript, I found a helpful guide in this blog post (). Despite following the outlined steps, I keep encountering an error message saying "Type 'string&ap ...

What are some ways to conceal methods within a class so that they are not accessible outside of the constructor

I am a newcomer to classes and I have written the following code: class BoardTypeResponse { created_on: string; name: string; threads: string[]; updated_on: string; _id: string; delete_password: string; loading: BoardLoadingType; error: Bo ...

Using TypeScript, a parameter is required only if another parameter is passed, and this rule applies multiple

I'm working on a concept of a distributed union type where passing one key makes other keys required. interface BaseArgs { title: string } interface FuncPagerArgs { enablePager: true limit: number count: number } type FuncArgs = (Fu ...

Generating and setting an object property in TypeScript at runtime

In my code, I have defined an interface as follows: export interface OurHistory { ourHistory?: object; step1?:object; step2?:object; } Within the HistoryComponent class, I am doing the following: export class HistoryComponent implements OnInit, On ...

Commitments, the Angular2 framework, and boundary

My Angular2 component is trying to obtain an ID from another service that returns a promise. To ensure that I receive the data before proceeding, I must await the Promise. Here's a snippet of what the component code looks like: export class AddTodoCo ...

Error encountered while utilizing the Extract function to refine a union

I am currently working on refining the return type of my EthereumViewModel.getCoinWithBalance method by utilizing the Extract utility type to extract a portion of my FlatAssetWithBalance union based on the generic type C defined in EthereumViewModel (which ...

The error message "tsc not found after docker image build" appeared on the

My goal is to deploy this program on local host. When I manually run "npm run build-tsc," it works successfully. However, I would like Docker to automatically run this command when building the image. Unfortunately, I receive an error saying that tsc is no ...

Implementing a NextJS client component within a webpage

I am currently working with NextJS version 14 and I am in the process of creating a landing page. In one of the sections, I need to utilize the useState hook. I have specified my component as "use-client" but I am still encountering an error stating that " ...

Add the list of information during the looping process (map)

I am currently facing a challenge where I need to update my data during the iteration and send the results to an API call. The API Call expects a request with data structured in the following format: { list: [{ id: "1", name: "Hello" ...

What is the best way to initiate a dialog within the handleSubmit function?

In my project, I have a component called SimpleDialog which is defined in the File.tsx file. export default function SimpleDialog() { const handleSubmit = (event: any) => { <SimpleDialog />; } return( <form> <Button type="submit& ...

Is it possible to bind an http post response to a dropdown list in Angular?

My goal is to connect my post response with a dropdown list, but the text displayed in the drop-down box shows "[object Object]". My request returns two results - `ArticleID` and `Title`. I want to display `Title` in the dropdown and save the corresponding ...

How to implement a reusable module with distinct routes in Angular

In my current angular project, we have various menus labeled A, B, C, D, and E that all utilize the same module. Specifically, menus A, C, and E use the same component/module. My goal is to ensure that when I am on menu A and then click on menu C, the sa ...

Group records in MongoDB by either (id1, id2) or (id2, id1)

Creating a messaging system with MongoDB, I have designed the message schema as follows: Message Schema: { senderId: ObjectId, receiverId: ObjectId createdAt: Date } My goal is to showcase all message exchanges between a user and other users ...

When trying to set the focus on the first item in a list using HTML and Angular, the focus unexpectedly shifts to the second

I've been tackling a UI requirement where the focus needs to be set on the first element of a list item constructed from an array of objects when the tab key is pressed for the first time. Subsequent tab key presses should cycle through the list items ...

Encountering an issue with TS / yarn where an exported const object cannot be utilized in a consuming

I am currently working on a private project using TypeScript and Yarn. In this project, I have developed a package that is meant to be utilized by one or more other applications. However, as I started to work on the consumer application, I encountered an ...

Bug in auto compilation in Typescript within the Visual Studios 2015

Currently, I am utilizing Visual Studio Pro 2015 with auto compile enabled on save feature. The issue arises in the compiled js file when an error occurs within the typescript __extends function. Specifically, it states 'Cannot read property prototyp ...

The property y is not found on type x during property deconstruction

After creating a straightforward projectname.tsx file to contain my interfaces/types, I encountered an issue: export interface Movie { id: number; title: string; posterPath: string; } In another component, I aimed to utilize the Movie interface to s ...

Include form data into an array of objects within an Angular data source

I am struggling to add the edited form data from edit-customers-dialog.ts into an array of objects in my datasource. The form.data.value is returning correctly, but I'm facing issues with inserting it properly into the array. As a beginner in Angular ...