Adding images to chart labels in vue-chartjs explained

I'm facing a challenge in adding flag icons below the country code labels, and I could really use some guidance.

Here is an image of the chart with my current code

The flag images I need to use are named BR.svg, FR.svg, and MX.svg, located under @/assets/icons/flags/

In my project, I am utilizing

<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="6117140421534f574f5053">[email protected]</a>
and
<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="86f0f3e3abe5eee7f4f2ecf5c6b5a8b3a8b7">[email protected]</a>
. Below is my Chart.vue component:

<script>
import { Bar } from 'vue-chartjs'

export default {
  extends: Bar,
  data: () => ({
    chartdata: {
      labels: ['BR', 'FR', 'MX'],
      datasets: [
        {
          label: 'Lorem ipsum',
          backgroundColor: '#AF78D2',
          data: [39, 30, 30],
        }
      ]
    },
    options: {
      responsive: true,
      maintainAspectRatio: false,
      legend: {
        display: false,
      },
      tooltips: {
        "enabled": false
      },
      scales : {
        xAxes : [ {
            gridLines : {
                display : false
            }
        } ],
        yAxes: [{
          ticks: {
            beginAtZero: true,
            suggestedMin: 0,
            suggestedMax: 40,
            stepSize: 5,
          }
        }]
      },
      "hover": {
        "animationDuration": 0
      },
      "animation": {
        "duration": 1,
        "onComplete": function() {
          var chartInstance = this.chart,
          ctx = chartInstance.ctx;

          ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
          ctx.textAlign = 'center';
          ctx.textBaseline = 'bottom';

          this.data.datasets.forEach(function(dataset, i) {
            var meta = chartInstance.controller.getDatasetMeta(i);
            meta.data.forEach(function(bar, index) {
              var data = dataset.data[index] + '%';
              ctx.fillText(data, bar._model.x, bar._model.y - 5);
            });
          });
        }
      },
    }
  }),
  mounted () {
    this.renderChart(this.chartdata, this.options)
  }
}
</script>

The code snippet below is the closest solution I found after hours of searching. Unfortunately, integrating it into my existing code has proven challenging.

const labels = ['Red Vans', 'Blue Vans', 'Green Vans', 'Gray Vans'];
const images = ['https://i.stack.imgur.com/2RAv2.png', 'https://i.stack.imgur.com/Tq5DA.png', 'https://i.stack.imgur.com/3KRtW.png', 'https://i.stack.imgur.com/iLyVi.png'];
const values = [48, 56, 33, 44];

new Chart(document.getElementById("myChart"), {
  type: "bar",
  plugins: [{
    afterDraw: chart => {      
      var ctx = chart.chart.ctx; 
      var xAxis = chart.scales['x-axis-0'];
      var yAxis = chart.scales['y-axis-0'];
      xAxis.ticks.forEach((value, index) => {  
        var x = xAxis.getPixelForTick(index);      
        var image = new Image();
        image.src = images[index],
        ctx.drawImage(image, x - 12, yAxis.bottom + 10);
      });      
    }
  }],
  data: {
    labels: labels,
    datasets: [{
      label: 'My Dataset',
      data: values,
      backgroundColor: ['red', 'blue', 'green', 'lightgray']
    }]
  },
  options: {
    responsive: true,
    legend: {
      display: false
    },    
    scales: {
      yAxes: [{ 
        ticks: {
          beginAtZero: true
        }
      }],
      xAxes: [{
        ticks: {
          padding: 30
        }   
      }],
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart" height="90"></canvas>

When attempting to add the plugin code to my own, I encounter an error message stating that 'plugins' is already defined in props, but I haven't been able to resolve this issue. Any insights on implementing the afterDraw plugin into my code would be greatly appreciated.

Thank you in advance! :)

Answer №1

Within the mounted hook of your Vue component, you have the ability to invoke the addPlugin method (which must be executed before the renderChart method) in this manner:

this.addPlugin({
  id: 'image-label',
  afterDraw: (chart) => {      
      var ctx = chart.chart.ctx; 
      var xAxis = chart.scales['x-axis-0'];
      var yAxis = chart.scales['y-axis-0'];
      xAxis.ticks.forEach((value, index) => {  
        var x = xAxis.getPixelForTick(index);      
        var image = new Image();
        image.src = images[index],
        ctx.drawImage(image, x - 12, yAxis.bottom + 10);
      });      
    }
})

For further details, please refer to the documentation:

Answer №2

If you are using ChartJS version 4.0.1, make sure to include the following code snippet in your data:

data() {
    return {
        plugins: [{
            afterDraw: chart => {
                    const ctx = chart.ctx;
                    const xAxis = chart.scales['x'];
                    const yAxis = chart.scales['y'];
                    xAxis.ticks.forEach((value, index) => {
                        let x = xAxis.getPixelForTick(index);
                        ctx.drawImage(images[index], x - 12, yAxis.bottom + 10)
                    });
                }
        }],
        data: {...

Remember to use chart.ctx instead of chart.chart.ct and chart.scales['x'] instead of chart.scales['x-axis-0'].

Once you have included the plugins in your data, reference it in your Bar component like this:

<Bar :data="data" :options="options" :plugins="plugins"/>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for triggering the JavaScript function within dynamically created textboxes on an ASP .NET platform

I have developed code that dynamically creates textboxes in a modal pop-up each time the add button is clicked and removes them when the remove button is clicked. The validation function in this code checks for valid month, date, and year entries in the te ...

What is the best way to navigate through the underlying MatDialog while the MatSelect is active?

When attempting to customize the scroll behavior of a MatSelect in a regular page, I discovered that using the MAT_SELECT_SCROLL_STRATEGY injection token with the NoopScrollStrategy allows for scrolling the underlying page while keeping the MatSelect stati ...

Trouble displaying background image in Electron Application

When I try to load an image file in the same directory as my login.vue component (where the following code is located), it won't display: <div background="benjamin-child-17946.jpg" class="login" style="height:100%;"> A 404 error is popping up: ...

Redux - Preventing Overwriting of Product Quantity in Cart by Creating a New Object

Is there a way to address the issue where adding the same product multiple times to the cart creates new objects instead of increasing the quantity? switch (action.type) { case actionTypes.ADD_TO_CART: const product = state.products.find((p) = ...

Exploring the differences between utilizing request.body in building RESTful APIs with Django versus Node.js

As I dive into learning the Django framework, my main aim is to leverage this knowledge in creating a rest api. Although I've looked into using django-rest framework, my current job necessitates a focus on Django specifically. In my journey so far, I ...

Creating a new dynamic page can be achieved by clicking on a dynamically generated link. Would you like to learn how to do that?

Recently, I developed a custom API using Node.js to retrieve information about blogs from Medium.com. The API currently provides: The author/main picture of the article Title A link to the article on medium.com (redundant) The entire article text in the ...

Executing a JavaScript function within PHP code

I am working on a foreach loop that creates a series of checkboxes with items from a database. Currently, none of the checkboxes are pre-checked and each time a user checks one, a JavaScript function is called. Now, my clients want the first checkbox to b ...

Arranging an array in alphabetical order, with some special cases taken into consideration

Below is a breakdown of the array structure: [{ name: "Mobile Uploads" }, { name: "Profile Pictures" }, { name: "Reports" }, { name: "Instagram Photos" }, { name: "Facebook" }, { name: "My Account" }, { name: "Twi ...

Modify mouse pointer when an object is clicked using JavaScript

Greetings, I am in the process of designing a website for a client. I have encountered a challenge in changing the cursor icon when a user performs a mousedown on an object. There is an image on the webpage When the user clicks on the image, the cursor s ...

Ways to retrieve only the chosen option when the dropdown menu features identical names

How can I retrieve only the selected value from the user, when there are multiple select options with the same class name due to dynamic data coming from a database? The goal is to submit the data using Ajax and have the user select one option at a time. ...

Remove every other element from a JSON Array by splicing out the even-numbered items, rather than removing matching items

After receiving a JSON Array Output from a REST API, I am using ng-repeat to display the items on an HTML page. The structure of the received data is as follows: var searchresponse = [{ "items": [{ "employeeId": "ABC", "type": "D", "alive": "Y ...

Is it necessary to implement the useCallback hook along with React.memo for rendering optimization in React components?

As I delved into understanding how useCallback and useMemo function in React to optimize app performance, I decided to create a simple app for experimentation. What I observed was that the callback function (OnBlurHandler) passed to my child component trig ...

What is preventing jQuery 3 from recognizing the '#' symbol in an attribute selector?

After updating my application to jQuery 3, I encountered an issue during testing. Everything seemed to be working fine until I reached a section of my code that used a '#' symbol in a selector. The jQuery snippet causing the problem looks like th ...

Tips for accessing a value from a setInterval function

Is it possible to retrieve a value from the setinterval function in JavaScript? $.ajax({ type : "POST", url:"<?php echo TESTMINE_APP_URL; ?>/ajax/export-details", data:'paginationHash='+paginationHash+'&exp ...

Is there a way to retrieve two arrays in an Ajax request?

JAVASCRIPT CODE: $.ajax({ url: 'assignavailtrainers.php', data: {action:'test'}, type: 'post', success: function(data) { } }); PHP CODE: <?php $username = "trainerapp"; ...

Experiencing difficulties while attempting to organize an array?

// const first = data.groups_with_selected[7]; // const second = data.groups_with_selected[20]; // data.groups_with_selected.splice(2, 0, first, second); // data.groups_with_selected.splice(9, 1) // data.groups_with_selected ...

The v-data-table-server is failing to refresh the component within the table template

I am utilizing a Vuetify v-data-table-server (docs) that dynamically updates itself by fetching data from an API. <template> <v-data-table-server v-if="events.length > 0" v-model:items-per-page="events_limit" : ...

Combine filter browsing with pagination functionality

I came across a pagination and filter search online that both function well independently. However, I am looking to merge them together. My goal is to have the pagination display as << [1][2] >> upon page load, and then adjust to <<[1]> ...

Display toggle malfunctioning when switching tabs

I am currently working on implementing a search function with tabbed features. Everything seems to be displaying correctly without any errors. However, the issue arises when I try to click on any tab in order to show or hide specific content from other tab ...

Leverage child component methods in parent component using Vue.js with CDN assets

Recently, I made the switch from Alpine.js to Vue.js (CDN) in order to experiment with async functions. My objective is to call a method from the parent component that is defined within a child component. However, when attempting to call "test()" ...