1

I'm been trying to pass code that works perfect in simple D3 to Vue. This part of the code supposed to update the label text of the barchart but is not working. This is data.csv:

country,population
China,2535
India,1654
United States,700
Indonesia,680
Brazil,1785
Total,1821

and this is code the manage the barchart:

import formatCurrency from '../mixins/formatCurrency';
import formatTime from '../mixins/formatTime';

import {
    select,
    scaleLinear,
    max,
    scaleBand,
    interpolate,
    axisLeft,
    axisBottom,
    forceX,
    csv,
    filter
} from 'd3';


//..
data: () => ({
        dataset: [],
        dataFiltrada: [],
    }),
//..

async mounted() {
        let datos = await csv('/datasets/data.csv')
        this.dataset = Object.freeze(datos)
        //..
        this.graph()
    },
computed: {
    //..
    filtrarData() {
       this.dataFiltrada = this.dataset.filter(d => { return d.country !=='Total'})},
},

methods: {
  graph(){
//..
    const numeros = g.selectAll(this.label).data(this.dataFiltrada)
    numeros
       .enter().append("text")
         .attr("class", this.label)                        
         .attr('x', this.xEtiqueta)
         .attr('y', d => { return -yScale(d.country); })
         .attr('dy', this.yEtiqueta)
         .text(0)                 

      .merge(numeros)
        .transition().duration(this.time)
        .tween(this.label, d => {
             var i = interpolate(1, d.population);
             if (d.population != 0)
             return function(t) {    
                 select(this).text(d => {
                     return (this.label === 'labelg1')                                            
                            ? this.formatCurrency(i(t))
                            : this.formatTime(i(t))
}                                    
//..
mixins: [
        formatCurrency,
        formatTime,
    ],
}

The transitions are ok, but the format is not updating. When I console.log formatCurrency with any value outside function(t) it's ok but inside scoped of function(t) is not working. The formatCurrency function is like this:

import { format } from 'd3';

export default {
    methods: {
        formatCurrency:(() => {
          var cantNum;
          var formatofinal;
          var simbol = "$";

          function digits_count(n) {
              var count = 0;
              if (n >= 1) ++count;

              while (n / 10 >= 1) {
                  n /= 10;
                  ++count;
              }
              return count;
          };

          function processCantNumAndFormatOfinal(n){
              if (digits_count(n) === 0) {
                  cantNum = 0;
                  formatofinal ='r';
              }
              else if (digits_count(n) === 1) {
                  cantNum = 1;
                  formatofinal ='s';
              }
              else if (digits_count(n) === 2) {
                  cantNum = 2;
                  formatofinal ='s';
              }
              else if (digits_count(n) === 3) {
                  cantNum = 3;
                  formatofinal ='s';
              }
              else if (digits_count(n) === 4) {
                  cantNum = 2;
                  formatofinal ='s';
              }
              else if (digits_count(n) === 5) {
                  cantNum = 3;
                  formatofinal ='s';
              }
              else if (digits_count(n) === 6) {
                  cantNum = 3;
                  formatofinal ='s';
              }
              else if (digits_count(n) === 7) {
                  cantNum = 2;
                  formatofinal ='s';
              }
              else if (digits_count(n) === 8) {
                  cantNum = 3;
                  formatofinal ='s';
              }
              else if (digits_count(n) === 9) {
                  cantNum = 3;
                  formatofinal ='s';
              }
              else {
                  cantNum = 2;
                  formatofinal ='s';
              };
          }

          function formatear(n) {
            // Process cantNum and formatofinal here ... function call
            processCantNumAndFormatOfinal(n);
            const formato = simbol + format(",."+ cantNum +formatofinal)(n)
                .replace('.', ',')
                .replace('G', 'B');
                return formato;
          };

          return function(n)
          {
            return formatear(n);
          }
        }
    })()
}

I have tried arrow function also but the data goes to zero.

.merge(numeros)
     .transition().duration(this.tiempo)
     .tween(this.label, d => {
         var i = interpolate(1, d.population);
         if (d.population != 0)
         return (t) => { //..}

EDITED:

Now I've noticed 2 things:

  1. this.label is not recognize inside function(t). When I transform this:
(this.label === 'labelg1')

into this:

(this.label != 'labelg1')

the condition is recognized but the error message I've received now is:

Uncaught TypeError: _this3.formatCurrency is not a function

EDITED 2:

I've changed the approach for formatCurrency, this is the new structure:

import { format } from 'd3';

export default {
    methods: {
        formatCurrency(n) {
           let count = digitsCount(n)
           let numeros = processCantNumAndFormatOfinal(count)[0]
           let formatoF = processCantNumAndFormatOfinal(count)[1]
           let final = formatear(numeros, formatoF, n)
           console.log(final)
           return final
       }
    }
}

function digitsCount(n) {
            let count = 0;
            if (n >= 1) ++count;

            while (n / 10 >= 1) {
              n /= 10;
              ++count;
            }
            return count;
        }

function processCantNumAndFormatOfinal(n) {
            let cantNum;
            let formatofinal;
            if (n === 0) {
              cantNum = 0;
              formatofinal ='r';
            }
            else if (n === 1) {
              cantNum = 1;
              formatofinal ='s';
            }
            else if (n === 2) {
              cantNum = 2;
              formatofinal ='s';
            }
            else if (n === 3) {
              cantNum = 3;
              formatofinal ='s';
            }
            else if (n === 4) {
              cantNum = 2;
              formatofinal ='s';
            }
            else if (n === 5) {
              cantNum = 3;
              formatofinal ='s';
            }
            else if (n === 6) {
              cantNum = 3;
              formatofinal ='s';
            }
            else if (n === 7) {
              cantNum = 2;
              formatofinal ='s';
            }
            else if (n === 8) {
              cantNum = 3;
              formatofinal ='s';
            }
            else if (n === 9) {
              cantNum = 3;
              formatofinal ='s';
            }
            else {
              cantNum = 2;
              formatofinal ='s';
            }
            return [cantNum,formatofinal]
        }

function formatear(cantNum, formatofinal, n) {

            let formato = format(",."+ cantNum + formatofinal)(n)

            return formato;
         }

And for component:

<template>

</template>

<script>

import formatCurrency from '../mixins/formatCurrency';
import formatTime from '../mixins/formatTime';

//...
async mounted() {
        let datos = await csv('/datasets/data.csv')
        this.dataset = Object.freeze(datos)
        this.dataNumero
        this.filtrarData
        this.graph()
        this.formatearData
    },
//...
computed: {
//...
        dataNumero() {
            this.dataset.forEach( function(d) { return d.population = +d.population})
        },
        filtrarData() {
            this.dataFiltrada = this.dataset.filter(function(d) { return d.country !== 'Total'})
        },
        formatearData() {
            this.dataFiltrada.forEach( function(d) { return d.population = this.formatCurrency(d.population) })
        },

methods: {
        graph() {
        //..
        const numeros = g.selectAll(this.label).data(this.dataFiltrada)
                numeros
                    .enter().append("text")
                        .attr("class", this.label)
                        /* esto les da posición a las etiquetas en x e y */
                        .attr('x', this.xEtiqueta)
                        .attr('y', d => { return -yScale(d.country); })
                        .attr('dy', this.yEtiqueta)
                        .text(0)

                    /* esto agrega las transiciones */
                    .merge(numeros)
                        .transition().duration(this.tiempo)
                        .tween(this.label, function(d) {
                            var i = interpolate(1, d.population);
                            if (d.population != 0)
                            return function(t) {
                                select(this).text(Math.round(i(t)))
                            };
                        })
//..
    mixins: [
    formatCurrency,
    formatTime
    ],


With arrow function in formatearData() {this.dataFiltrada.forEach(d => //.. the console shows the results ok, but the chart shows NaN values in all numbers higher than 3 digits(?????), when I changed formatearData() removing the arrow function formatearData() {this.dataFiltrada.forEach(function(d) { d.population = //.. the error is : Error in mounted hook (Promise/async): "TypeError: Cannot read property 'formatCurrency' of undefined" Below is the image for reference. enter image description here

EDITED 3:

Ok, the function that is not working is d3 format, the problem is that I have tried to format the text label number of the d3 bar chart with special format but when this happens the numbers transform into strings and renders a NaN value, so the relevant question is: is there any way to format D3 bar chart labels numbers in vue during a tween transition or another method?, because this code works perfect with simple javascript and webpack, but in vue is a different story.

2 Answers2

0

avoid arrow functions to use this keyword.Use as:

select(this).text(function(d){
    return (this.label === 'labelg1')                                            
         ? this.formatCurrency(i(t))                   
          : this.formatTime(i(t))                                   
} 

For more details Read this.

tuhin47
  • 5,172
  • 4
  • 19
  • 29
0

I have found this library numeral.js, my code now looks like this:

methods: {
        graph() {
              ...
              var numeral = require("numeral");
              const sin = this;
              ...
              const numeros = g.selectAll(this.label).data(this.dataFiltrada)
                numeros
                    .enter().append("text")
                        .attr("class", this.label)
                        /* esto les da posición a las etiquetas en x e y */
                        .attr('x', this.xEtiqueta)
                        .attr('y', function(d) { return -yScale(d.Segmento); })
                        .attr('dy', this.yEtiqueta)
                        .text(0)

                    /* esto agrega las transiciones */
                    .merge(numeros)
                        .transition().duration(this.tiempo)
                        .tween(this.label, function(d) {
                            var i = interpolate(0, d.Valor);
                            if (d.Valor != 0)
                            return function(t) {
                                const formatoNum = sin.formatoNum;
                                select(this).text(numeral(i(t)).format(formatoNum))
                            }
                        });

Another import thing is to set the component in Home.vue like this:

<template>
...
<graph id="graficoBarras1" v-bind='props1' :dataFiltrada="data1" v-if="data1.length" :key="gbKey"/>
...

data() {
        return {
            ...
            props1: {
                ...
                formatoNum      : '$0.0a'
            },
            ...
            data1       : [],
            gbKey       : 0,
            ...
methods: {
        forceRerender() {
          this.gbKey += 1;
        },

With this my transitions finally works.