0
votes

I am using angular-highcharts (https://www.npmjs.com/package/angular-highcharts), I am able to detect events, however cannot seem to load a function.

The event capture is for clicking on any of the series' points...

events: {
    click: function(event) {
        alert("Highcharts event click detected... Next action will be to load 'myFunction'.. check the console for error");
        this.myFunction();
    }
}

It should load "myFunction()" which is a simple alert.

myFunction() {
    alert("myFunction executed");
}

Instead, the error in console log is:

> ERROR TypeError: this.myFunction is not a function

This works fine in "vanilla" Javascript, but not in angular-highcharts.

angular (not-working):

https://stackblitz.com/edit/angular-8v1obg

vanilla (working):

http://jsfiddle.net/marty232/kkgygaya/3/

How can i get the angular version to load a function?

Update
Q: Why do I want to be able to load a function?
A: When user clicks on a data point, I need to trigger various external services, change variables, update HTML etc. So, even though my example is to access one single function, I need to basically be able to access "any" function or object in the "this" scope (and ideally, other scopes too). Hope that makes sense.

1
when do you get this errorSajeetharan
The problem probably is that at the time of execution your click handler, the reference to "this" has changed. While in your vanilla example you reference a function that is in the scope of the call, your "this" scope changes in the angular example, therefore it can't find the function. I'm not sure how to properly fix this though.Benedikt Schmidt

1 Answers

4
votes

The problem seems to be that you are losing your scope when executing the click event in your angular example. The reference to this at this point of execution is different from what you are expecting. In your vanilla example, you are referencing a function that is in the same scope.

One solution could be not to bind your function to execute to your component itself but rather create it as a local variable inside the block where you init the highcharts.

init() {
  // create the function in the same scope so it does not have to rely on "this"
  var myFunction=()=> {
     alert("myFunction is now properly executed");
  }

  let chart = new Chart({
    ..
    plotOptions: {
      series: {
        lineWidth: 2,
        events: {
          click: function(event) {
            alert("Highcharts event click detected... Next action will be to load 'myFunction'.. check the console for error");
            myFunction();          }
        }
      },
    },
    ...
  });
  ...
}

The problem then is that the function does not exist outside of the init block anymore, so you can't call it from anywhere else. Then again the question is if this is necessary.

EDIT

So I tried some tricky stuff and somewhere out there someone probably wants to punch me in the face for this, but hey, it's just a suggestion.

What we can do to keep the function tied to the component, so you can still call it from the HTML for example, but also be able to use it inside the chart click event, is to provide it to your init() function as a parameter. The more I think about it, the more I think it's dirty as hell. Here's what it looks like then.

myFunction() {alert("myFunction executed");}

ngOnInit() {
  // transport the function
  this.init(this.myFunction);
}

init(myFunction) {
  // now it is untied from the "this" scope
  let chart = new Chart({
    chart: {
      type: 'line'
    },
    plotOptions: {
      series: {
        lineWidth: 2,
        events: {
          click: function(event) {
            alert("Highcharts event click detected... Next action will be to load 'myFunction'.. check the console for error");
            // use the variable instead of the private function
            myFunction();          }
        }
      },

    },
    ...
  });
  ...
}

Ultimately when called from the chart you won't be able to do stuff inside your function that relates to the components context, for example changing variables or trying to access private services. So in the end it depends on what you want to do with it.

EDIT 2

Maybe this one is a bit cleaner. Instead of providing the function without scope, we tie the whole scope into a variable. This is usually done by just setting a variable like var that=this.

init() {
  // let's save the whole scope in a variable, so it is know to the functions scope
  let componentScope = this;

  let chart = new Chart({
    chart: {
      type: 'line'
    },
    plotOptions: {
      series: {
        lineWidth: 2,
        events: {
          click: function(event) {
            alert("Highcharts event click detected... Next action will be to load 'myFunction'.. check the console for error");
            // use the component scope here
            componentScope.myFunction();          }
        }
      },

    },
    ...
  });
  ...
}

I think this should do the trick. I think saving the whole scope means you can also access everything else in it, for example private component functions, varaibles, services etc.