0

Okay, so in my program, I'm having to supply a callback as an event listener as part of the init() method of my class, and I'd like this callback to be another method from the same class (onPageShown(ev) from code below).

const pageShownEvent = new Event("pageShown");

class App
{

    private pages: NodeListOf<HTMLDivElement>;

    public onPageShown(ev: Event) {
        console.log("Method called as callback");
    }

  
    public init() {
        this.pages = <NodeListOf<HTMLDivElement>> document.querySelectorAll(".page");
        console.log(this.pages);

        for(let i = 0; i < this.pages.length; ++i) 
        {
            this.pages[i].addEventListener("pageShown", this.onPageShown);
        }

        /*this.pages.forEach((pg: HTMLDivElement) => {
            pg.addEventListener("pageShown", this.onPageShown);
            
        });*/
        
    }

    // singleton instance
    private static instance: App;

    private constructor() {
        this.pages = null
    }

    // ensure only 1 instance is created
    public static getInstance() {
        if(!App.instance) {
            App.instance = new App()
        }
        
        return App.instance;
    }

}

const myApp = App.getInstance();

However, the problem is that whenever I use the this pointer to access onPageShow like so, it doesn't seem to be added or interpreted as a callback function. No errors are thrown and auto-completion still works as expected, hinting that this is still pointing at the right object.

this.pages[i].addEventListener("pageShown", this.onPageShown);

The only way I get it to work is by explicitly supplying onPageShown through the class instance given by getInstance() (its a singleton class), in which case it works perfectly and I see the correct output in the console.

this.pages[i].addEventListener("pageShown", App.getInstance().onPageShown);

At first I thought this was pointing to the wrong object as I was using the callback implemented forEach(), but even after I switcher to using a regular for-loop, the problem persisted.

It may seem that my current solution works just as well, but it requires me to set the methods I want to use as callbacks to public, when I'd much rather have them be private (this should have no problem accessing private member, should it not?).

If anyone would be able to provide some wisdom, it would be much appreciated.

EDIT: I was wrong, it seems both ways of obtaining a pointer to the current object allow me to call private functions as long as its done within said object. However, I still don't know why out of my two code snippets, one works and one doesn't.

EDIT 2: compiled JS code that throws error "uncaught TypeError: this.onPageShown is not a function at HTMLDocument.App.init"

var pageShownEvent = new Event("pageShown");
var App = /** @class */ (function () {
    function App() {
        this.pages = null;
    }
    App.prototype.onPageShown = function (ev) {
        console.log("Method called as callback");
    };
    App.prototype.navToPage = function (pageId) {
        console.log("Nav to page " + pageId);
        document.querySelector(".active").classList.remove("active");
        document.getElementById(pageId).classList.add("active");
        document.getElementById(pageId).dispatchEvent(pageShownEvent);
    };
    App.prototype.init = function () {
        this.pages = document.querySelectorAll(".page");
        console.log(this.pages);
        for (var i = 0; i < this.pages.length; ++i) {
            this.pages[i].addEventListener("pageShown", App.getInstance().onPageShown);
            /*
               TypeError thrown here
            */
            this.onPageShown(null); 
        }
        /*this.pages.forEach((pg: HTMLDivElement) => {
            pg.addEventListener("pageShown", App.getInstance().onPageShown);
            
        });*/
        document.querySelectorAll(".nav-link").forEach(function (link) {
            link.addEventListener("click", function (ev) {
                //console.log("click");
                ev.preventDefault;
                var target = ev.target;
                var currentPage = target.getAttribute("data-target");
                console.log(currentPage);
                App.getInstance().navToPage(currentPage);
                /*document.querySelector(".active").classList.remove("active");
                document.getElementById(currentPage).classList.add("active");

                document.getElementById(currentPage).dispatchEvent(pageShownEvent);*/
                //history.replaceState({}, currentPage, `#${currentPage}`);
            });
        });
        //history.replaceState({}, "1", "#1");
    };
    // ensure only 1 instance is created
    App.getInstance = function () {
        if (!App.instance) {
            App.instance = new App();
        }
        return App.instance;
    };
    return App;
}());
HervéSV
  • 27
  • 1
  • 5
  • I do not see any problem with the code. I don't understand the part "it doesn't seem to be added or interpreted as a callback function.". Can you elaborate? Code runs but callback will never get called? Did you debug the code? Set a break-point on the line that adds the callback, and see what you have there. – Mohammad Dehghan Jan 16 '21 at 14:15
  • I can't seem to get break-points to work. However, I tried calling the method using ```this```, and it throws an error stating that said function doesn't exist. I'm fairly sure it has to do with the generated js code. At this point however, I've just settled with explicitly getting it from my singleton instance since it pretty much does what I want it to. I don't know if I can mark this post as resolved, but I'll post the compiled js code if anyone is interested or has a similar problem. – HervéSV Jan 18 '21 at 09:52
  • Where are you *calling* `init`…? – deceze Jan 18 '21 at 10:21

0 Answers0