Ilja
Ilja

Reputation: 46479

Angular 2 can't call class function

inside my class I have a simple function that logs errors called logError(error);

I also use google maps api to grab latitude and longitude of an address, so all together this looks like this inside my exported class.

  //Log error
  logError(err) {
   console.error('Error: ' + err);
  }

  //Get Coordinates from address
  postcodeCoordinates(address: string, postcode: string) {
    var geocoder =  new google.maps.Geocoder();
    geocoder.geocode( { 'address': address + ', ' + postcode}, function(results, status) {
      if (status == google.maps.GeocoderStatus.OK) {
        console.log(results[0].geometry.location.lat() + " " +results[0].geometry.location.lng());
      } else {
       this.logError("Error " + status);
      }
    });
  }

The issue is that I get console error saying Uncaught TypeError: Cannot read property 'logError' of undefined , but this function works perfectly fine called in a same way from other places in the code, which makes me thing that this in this.logError("Error " + status); refferes to google maps api, instead of my class. How can I work around this? is there a way to achieve something like MyClassName.logError("Error") (Tried this, doesn't work)

Upvotes: 0

Views: 2062

Answers (1)

Thierry Templier
Thierry Templier

Reputation: 202206

You should use an arrow function, the this keyword will the one for your component and not the one of the callback function. Something like that:

geocoder.geocode( { 'address': address + ', ' + postcode}, (results, status) => {
  if (status == google.maps.GeocoderStatus.OK) {
    console.log(results[0].geometry.location.lat() + " " +results[0].geometry.location.lng());
  } else {
   this.logError("Error " + status);
  }
});

In this case, logError is a method of your current class.

To give you more details, when using the this keyword in a function, it corresponds to the object the function is executed on:

// Example #1

function test() {
  console.log(this);
}

test(); // prints null

// Example #2

var obj2 = {
  test: function() {
    console.log(this);
  }
};

obj2.test(); // prints obj2

// Example #3

var obj3 = {
  test: function() {
    console.log(this);
  }
}

var fct3 = obj3.test;
fct3() // prints null!!

// Example #4

var obj4 = {
  test: function() {
    geocoder.geocode({ ... }, function(results, status) {
      console.log(this);
    });
  }
};

obj2.test(); // doesn't print obj2

When you provide a callback, it will be executed as a function outside any object as a context or with a specific context (and not the caller one). This means that inside your callback the this keyword corresponds to the context your callback is executed on and sure, it's not the component instance.

It's a common problem in JavaScript application. Arrow functions aren't classical functions and introduce the "lexical this" concept. This means that they don't use their own this but use the one from the caller:

var obj2 = {
  test: function() {
    geocoder.geocode({ ... }, (results, status) => {
      console.log(this);
    });
  }
};

obj2.test(); // prints obj2

This link could help you: https://toddmotto.com/es6-arrow-functions-syntaxes-and-lexical-scoping/. See the section: "Functionality: lexical scoping this".

Hope it helps you, Thierry

Upvotes: 4

Related Questions