2

I have an Ionic 2 app that shows the user Items inside Packages in the form of a list. I created a function that retrieves the current Item index inside a Package, it is fired the user launches the app. It checks if the index name-value pair in Ionic Storage (internal) memory is null. In that case, the index is set to 0 and stored in both internal and working memory. Otherwise, the index is retrieved from internal memory and used to set working memory.

However, self.storage.set(myString, '0'); in the code below throws the error TypeError: Cannot call method 'set' of undefined. I checked using console.dir and indeed self.storage was undefined inside the then(...); block. Why is this, and how can I work around it?

Imports, declarations, constructor

import { Injectable } from '@angular/core';

import { Storage } from '@ionic/storage';

declare var self: ItemStorageService;

@Injectable()
export class ItemStorageService {
    workingMemoryItems: Array<any>;
    workingMemoryIndex: number;
    packageIndex: number;

    constructor(public storage: Storage) {
        self = this;
        self.packageIndex = 0;

        self.initializeIndex().then(
            res => {
                console.log(res);
            },
            err => {
                console.log(err);
            }
        )
    }
// Other methods including initializeIndex()
}

InitializeIndex function

public initializeIndex(): Promise<any> {
    console.dir(self.storage); // Returns Storage object
    var myString = 'index_item_package_' + self.packageIndex;
    return new Promise((resolve, reject) => {
        self.storage.get(myString).then(
            val => {
                if ((val === null) || (val === 'null')) {
                    console.dir(self.storage); // Returns undefined
                    self.storage.set(myString, '0');
                    self.workingMemoryIndex = 0;
                    resolve('Working memory index not found and set to 0');
                } else {
                    self.workingMemoryIndex = val as number;
                    resolve('Working memory index found and set to ' + val);
                }
            },
            err => {
                self.storage.set(myString, 0);
                self.workingMemoryIndex = 0;
                reject('Working memory index not found and set to 0');
            }
        )
    });
}
Voyna
  • 33
  • 6
  • Where are you setting `self`? – Saravana Aug 09 '17 at 17:13
  • I'm declaring it just below my imports, and setting `var self = this;` in my constructor – Voyna Aug 09 '17 at 18:40
  • 1
    Can you add that part to the question as well? And are you creating a new variable with `var` in constructor? Shouldn't you be assigning to the variable you already declared? – Saravana Aug 10 '17 at 01:46
  • My bad, I'm using the same variable. I added in my code for more clarity. – Voyna Aug 10 '17 at 08:40
  • 1
    Arrow functions are lexically bound, you can just use `this` here. – Benjamin Gruenbaum Aug 10 '17 at 09:12
  • I changed every instance of `self` to `this` and that fixed it! `console.dir` shows `this.storage` as defined both inside and outside the promise.. But I'm still confused as to why. I read that setting `self = this;` was a foolproof way of avoiding any scoping issues. How does my assignment break something? – Voyna Aug 10 '17 at 10:25

2 Answers2

0

I believe the reason why this happened is because the global variable self was declared at the top of every one of my pages and services. This means that when I was running self.storage.get(myString), the constructor for ItemStorageService was running and re-initializing self to point at itself.

The solution to this is not to use self everywhere, but only where it's needed (like inside callback functions) and to make sure to set self = this as close to where it's used as possible.

Voyna
  • 33
  • 6
0

Try to remove the self = this and just use this.

Like :

this.packageIndex = 0; and this.storage.get(myString).then(...

Alex Pappas
  • 2,377
  • 3
  • 24
  • 48