javascriptvue.jshighmaps

Vue.js - Highmaps - change variable's value from inside chart


First of all let me introduce you to my project. I am desining a web application that will show some data about devices scattered around a country. To create this I used Vue.js and HighCharts (HighMaps for the map part). This is the result I have achieved now.

What I want to add now is the possibility for the end-user to click on a marked region and show all of the devices in that region. To do that I need the region's "ID", called code in HighMaps, to send a ajax request to my db and I would also like to make this new "div" a component so that I can use it freely in my application. I'll put a sketch image of what I mean (excuse me for my really bad paint skills :D):

The black lines are not important, what I would like to achieve is to show a new component besides the map (or wherever really). Next is my current code, I am using the one page, one component style so both template and script tags are in the same file and I omitted in the script tag all the unecessary things. Right now I just set up a div with curly brackets to update a variable on change, just to debug more easily. My main problem is that, in the plotOptions.series.point.events.click when I try to reference the this.foo variable it doesn't set it since the div doesn't update. I think that might be a scope issue but I wouldn't know where to start looking.

<template>
  <div id="canvasContainer">
    <highmaps :options="chartOptions"></highmaps>
    <app-componentForDevices><app-componentForDevices>
    <div>{{foo}}</div>
  </div>
</template>

<script>
  import HighCharts from 'vue-highcharts';
  import json from '../map.json'

  export default {
    data: function () {
      return {
        foo: 'NO',
        /* Map */
        chartOptions: {
          chart: {
            map: json, // The map data is taken from the .json file imported above
          },
          plotOptions: {
            series: {
              point: {
                events: {
                  click: function () {
                    this.foo='OK'
                  }
                }
              }
            },
            map: {
              joinBy: ['hc-key', 'code'],
              allAreas: false,
              tooltip: {
                headerFormat: '',
                pointFormat: '{point.name}: <b>{series.name}</b>'
              },
            }
          },
          /* Zoom and move */
          mapNavigation: {
            enabled: true,
            buttonOptions: {
              verticalAlign: 'bottom'
            }
          },
          series: [
            {
              allAreas: true,
              showInLegend: false,
            },
            {
              borderColor: '#a0451c',
              cursor: 'pointer',
              name: 'ERROR',
              color: "red",
              data: ['it-na', 'it-mi', 'it-mo', 'it-ud'].map(function (code) {
                return {code: code};
              }),
            },
            {
              borderColor: '#a09e21',
              cursor: 'pointer',
              name: 'WARNING',
              color: "yellow",
              data: ['it-ts', 'it-av', 'it-ri'].map(function (code) {
                return {code: code};
              }),
            },
            {
              borderColor: '#4ea02a',
              cursor: 'pointer',
              name: "OK",
              color: "lightgreen",
              data: ['it-pa', 'it-ve', 'it-bo', 'it-ta', 'it-pn'].map(function (code) {
                return {code: code};
              }),
            },
          ],
        }
      }
    }
  }
</script>

<style scoped>
  svg{
    align-items: center;
  }

Thanks in advance for the help. Cheers!

EDIT

I have just tried @icecream_hobbit 's suggestion and using a ECMA6 arrow function helped since now I can access the variable store in Vue but now I lost the access to the local arguments like this.name which made possibile for me to print the selected region. Any ideas? Am I missing something?

EDITv2

Thanks to @icecream_hobbit I have found a way to do what I wanted. You just need to add an object inside the parenthesis so that you can use this for the global variable and e for your mouse click event.

events: {
           click: (e) => {
             this.foo = 'OK'
             this.foo = e.point.name
           }
}

Solution

  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

    Until arrow functions, every new function defined its own this value (a new object in the case of a constructor, undefined in strict mode function calls, the base object if the function is called as an "object method", etc.)

    The this you were accessing did not belong to the Vue instance. You can use an arrow function () => {/*function body*/} to inherit the this of the Vue instance.

    An arrow function expression has a shorter syntax than a function expression and does not have its own this, arguments, super, or new.target.

    EDIT: For the added question of how do I do I get the Vue instance, and the object instance at the same time you can use a closure.

    click: function(VueInstance){
        return function() {
            VueInstance.Foo = 'OK';
        }(this)
    }