Question

This is ALMOST the same as every other this scoping question I have read so far other than one slight difference which makes it relevant (imo) to ask this question.

Now originally my problem was scoping of this with Knockout and Typescript, so given the following:

class ViewModel
{
    public SomeObservableArray = new ko.observableArray();

    public AddToTheObservableArray(someJsonData: any) 
    {
        this.SomeObservableArray.push(new SomePojo(someJsonData));
    }
}

So the this bit in the above code would blow up because Typescript makes you think that this is the class instance, but in reality it is some other this because of an ajax callback or view element, whatever the scenario that overrides the this keyword.

So to fix that, most solutions are to move that code into the constructor for the class, which I personally find horrible, however this small amount of horror is acceptable given the other benefits I get from using TypeScript. So just so we are all on the same page, the code below fixes the above problem:

class ViewModel
{
    public SomeObservableArray = new ko.observableArray();
    public AddToTheObservableArray = (someJsonData: any) => Void;

    constructor
    {
        this.AddToTheObservableArray = (someJsonData: any) => {
            this.SomeObservableArray.push(new SomePojo(someJsonData));
        };
    }
}

I am just writing this example code off top of my head so I apologize for any typos etc, but it hilights the common issue faced and the common solution/work around.

NOW! the issue I have is the next step from here, I have code like so:

class ViewModel
{
    public SomeObservableArray = new ko.observableArray();
    public AddToTheObservableArray = (someJsonData: any) => Void;


    constructor
    {
        this.PopulateObservableArray = (someJsonArrayData: any) => {
            this.SomeObservableArray.removeAll();
            someJsonArrayData.forEach(function(someJsonData) {
                this.SomeObservableArray.push(new SomePojo(someJsonData));
            });
        };
    }
}

The outputted code looks like so:

var ViewModel = (function(){
    function ViewModel(){
        var _this = this;

        this.SomeObservableArray = new ko.observableArray();

        this.AddMultipleEntitiesToObservableArray = function(someJsonArrayData) {
            _this.SomeObservableArray.removeAll();
            someJsonArrayData.forEach(function(someJsonData) {
                this.SomeObservableArray.push(new SomePojo(someJsonData));
            });
        }
    };
    return ViewModel;
})();

Again I apologise for any typos as I am just simplifying the larger projects output, however the main thing to see here is that the forEach methods child this remains, so I get the error this.SomeObservableArray is undefined.

I am sure 1 possible solution is to extract the foreach out and make it its own method, however this then feels like sticking cellotape over blutack, so I was wondering if there is some more elegant solution or some wrongdoing on my part which can be changed to at least not have to make it more unreadable.

Was it helpful?

Solution

Yes there is, and it's actually pretty easy. Just use the lambda expression on any method you want to get scoped to the scope of the higher function. In your case you need to rewrite your example as:

class ViewModel
{
    public SomeObservableArray = new ko.observableArray();
    public AddToTheObservableArray = (someJsonData: any) => Void;


    constructor()
    {
        this.PopulateObservableArray = (someJsonArrayData: any) => {
            this.SomeObservableArray.removeAll();
            someJsonArrayData.forEach((someJsonData) => {
                this.SomeObservableArray.push(new SomePojo(someJsonData));
            });
        };
    }
}

PS: it's considered a best practice not to manipulate an observable array in a for each, as the subscribers of that array will be notified on each change. Just push your data to a temporary array and then set this array as the value of your observable array (just my 2cts.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top