3

I'm using typescript and knockoutjs. On a <select> value change, I need to call a function called updateCities.

I get this error in the console:

Uncaught TypeError: this.updateCities is not a function

Here is my code:

class HomeViewModel {
    _countries = ko.observableArray();
    _regions = ko.observableArray();
    selectedCountry = ko.observable();


    constructor(allCountries) {
        for (var index = 0; index < allCountries.length; index++) {
            this._countries.push(allCountries[index].country);
        }

        this.selectedCountry.subscribe(function (newValue){
            this.updateCities();
        }) //this is where it sets the function to call on value change
    }

    updateCities() {
        $.ajax({
            url: `http://api.geonames.org/children?geonameId=${selectedCountry}&username=elion`
        }).then(function(allCitiesXML) {
            var allCitiesJSON = xml2json(allCitiesXML);
            var allCities = JSON.parse(allCitiesJSON);
            for (var index = 0; index < allCities.length; index++) {
                this._regions.push(allCities[index]);
            }
        });
    }
}

When I view the generated javascript file, I see that updateCountries is outside of the scope of this. Here is the generated javascript file:

var HomeViewModel = (function () {
    function HomeViewModel(allCountries) {
        this._countries = ko.observableArray();
        this._regions = ko.observableArray();
        this.selectedCountry = ko.observable();
        for (var index = 0; index < allCountries.length; index++) {
            this._countries.push(allCountries[index].country);
        }
        this.selectedCountry.subscribe(function (newValue) {
            this.updateCities();
        });
    }
    HomeViewModel.prototype.updateCities = function () {
        $.ajax({
            url: "http://api.geonames.org/children?geonameId=" + selectedCountry + "&username=elion"
        }).then(function (allCitiesXML) {
            var allCitiesJSON = xml2json(allCitiesXML);
            var allCities = JSON.parse(allCitiesJSON);
            for (var index = 0; index < allCities.length; index++) {
                this._regions.push(allCities[index]);
            }
        });
    };
    return HomeViewModel;
})();

How do I cause the generated javascript file to call updateCities properly?

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
BeniaminoBaggins
  • 11,202
  • 41
  • 152
  • 287
  • I had an issue like this once. What happens if you do var foo = this; before the constructor, and then used foo.updateCities inside the constructor? – william.taylor.09 Feb 05 '16 at 20:27
  • @william.taylor.09 that causes "uncaught ReferenceError: allCountries is not defined" on line 10 of the js file. – BeniaminoBaggins Feb 05 '16 at 20:34
  • @william.taylor.09 it is referring to this line that it adds: `constructor(allCountries);` which has global scope – BeniaminoBaggins Feb 05 '16 at 20:38
  • What's calling HomeViewModel? Can you try export class HomeViewModel {? I'm completely shooting in the dark on this one.. sorry. – william.taylor.09 Feb 05 '16 at 20:45
  • The problem is that you call `this.updateCities` inside an anonymous subscribe callback, which is `this` in that context. Define `var self = this` inside the constructor, then call `self.updateCities` in the subscription. Actually you don't even need it. You could simply do `...subscribe(this.updateCities.bind(this))`(if you don't need IE8- support) – webketje Feb 05 '16 at 21:30
  • @Tyblitz Thanks. I did `subscribe(this.updateCities.bind(this))`. Now I need to also reference the viewmodel (this) inside the for loop in updateCities. How do I do that? – BeniaminoBaggins Feb 05 '16 at 21:57
  • well normally that's it... Just reference it with `this`. To test you can always `console.log(this)` – webketje Feb 05 '16 at 22:10

0 Answers0