javascriptecharts

The dataset component does not configure the legend


I'm configuring two charts and the legend only appears when I use series.data, but it doesn't appear when I use the dataset component (series.datasetIndex and series.encode). Nothing I tried worked. Here's the code:

   document.addEventListener("DOMContentLoaded", () => {

        // sytem
        const chartSystem = () => {
            return {
                "source": {
                    "first": [
                        ["name", "value"],
                        ["Pressure", 40],
                        ["Temperature", 64],
                        ["Atmosphere", 89]
                    ],
                    "second": [
                        ["name", "value"],
                        ["Label 1", 15],
                        ["Label 2", 68]
                    ]
                }
            }
        }

        // send
        const pullDataset = [];
        const pullData = [];

        const chartSend = () => {
            const { first, second } = chartSystem().source;

            pullDataset.push({
                source: first
                // sourceHeader: true
            });

            pullData.push(
                {
                    data: second.slice(1).map(([name, value]) => ({
                        name,
                        value
                    }))
                }
            );
        };

        chartSend();

        // frames
        const chartUse = echarts.init(document.getElementsByClassName("chart")[0]);

        function chartFrameSwitch0 () {

            const tooltip0 = {
                show: true
            };
            
            const useDataLegend = pullDataset[0].source.slice(1).map(item => item[0]);
            console.log(useDataLegend);

            // legend
            const legend0 = [
                {
                    show: true,
                    data: useDataLegend,
                    borderWidth: 2,
                    borderColor: 'red'
                },
                {
                    show: true,
                    data: pullData[0].data.map(item => item.name),
                    borderWidth: 2,
                    borderColor: 'blue',
                    left: 'center',
                    top: '5%'
                }
            ];

            const grid0 = [
                {
                    top: '30%',
                    left: '5%',
                    width: '38%',
                    height:'30%'
                }
            ];

            const xAxis0 = [
                {
                    gridIndex: 0,
                    type: 'category'
                }
            ];

            const yAxis0 = [
                {
                    gridIndex: 0,
                    type: 'value'
                }
            ];

            const series0 = [
                {
                    type: 'bar',
                    color: ['#49a6de', '#ff7500', '#ff00ff'],
                    colorBy: 'data',
                    datasetIndex: 0,
                    encode: {
                        x: 0,
                        y: 1
                    },
                    xAxisIndex: 0,
                    yAxisIndex: 0
                },
                {
                    type: 'pie',
                    legendIndex: 0,
                    center: ['70%', '50%'],
                    data: pullData[0].data
                }
            ];

            const option = {
                dataset: [pullDataset[0]],
                legend: legend0, // Keep both legends in the array
                tooltip: tooltip0,
                grid: grid0,
                xAxis: xAxis0,
                yAxis: yAxis0,
                series: series0
            };

            chartUse.setOption(option);
        }

        chartFrameSwitch0();

    })
<head>
    <script src='https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js'></script>
</head>
    
<div class='chart' style='width: 100%; height: 100vh;'></div>

See the console.log of useDataLegend:

[
  "Pressure",
  "Temperature",
  "Atmosphere"
]

This is a manual way I tried to set legend.data: [...]. I tried using series.encode, but it doesn't seem to support setting the legend.


Solution

  • The issue is not related to the source of the data, the same happens with the bar having a data entry instead of encoding.

    The problem is actually with how legend works for a bar chart - in a bar chart (as well as many other types of charts) you simply can't have a legend entry for each data item, but one legend entry for each data set. That of course is not the case for the pie chart, where each data item (slice) will have its legend entry.

    That has been the topic of this post and even if echarts has been updated a lot since then, this doesn't seem to have been changed.

    There's also an important point about the meaning of legend.data; although the docs on legend.data are a bit confusing, things are clarified in legend.data.name docs: you can't put any name in there to alter the name that appears for the legend. The string, (or the value of name property if it's an object) should be one of the actual names that appear by default on the legend, that is the name of a series for a bar chart and the name of a data item for a pie. A simple experiment will prove that: replace in your code:

    {
       show: true,
       data: pullData[0].data.map(item => item.name),
       borderWidth: 2,
       borderColor: 'blue',
       left: 'center',
       top: '5%'
    }
    

    the data with an altered name, like

       data: pullData[0].data.map(item => item.name + 'x'),
    

    we see that the pie legend also disappears (jsfiddle).

    Thus, the purpose of the names in data is just to select the legend items, for instance if a name of a series is missing, it will not have a legend entry.

    So, in order to have something displayed, in the bar legend we have to start by adding a name to the bar series:

    const series0 = [
       {
          type: 'bar',
          name: 'barbar',
          color: ['#49a6de', '#ff7500', '#ff00ff'],
          colorBy: 'data',
          ...............
    

    and add the same name in the corresponding legend.data:

    const legend0 = [
       {
          show: true,
          data: ['barbar'], // or data: [{name: 'barbar'],
          borderWidth: 2,
          borderColor: 'red'
       },
       {
          show: true,
          data: pullData[0].data.map(item => item.name),
          borderWidth: 2,
          borderColor: 'blue',
          left: 'center',
          top: '10%'
       }
    ]
    

    This will put just one item in the red bordered legend, for the only bar series, jsFiddle.

    It is important to select the items shown by the two legends through legend.data; if it were missing, the default for both legends would be to show all three available items, that is the one for the bar series and the two for the pie data items, (fiddle).

    To achieve what you initially wanted, we have to employ the trick presented in the answer to the post linked above, that is to put each bar in its own series:

    const chartSystem = () => {
       return {
          "source": {
             "first": [
                ["name", "value"],
                ["Pressure", 40],
                ["Temperature", 64],
                ["Atmosphere", 89]
             ],
             "second": [
                ["name", "value"],
                ["Label 1", 15],
                ["Label 2", 68]
             ]
          }
       }
    }
    
    // send
    const pullDataset = [];
    const pullData = [];
    const header = [];
    const chartSend = () => {
       const { first, second } = chartSystem().source;
    
       header.push(...first.slice(1).map(item => item[0]));
       pullDataset.push({
          source: [['name', ...header],
             ...Array.from({length: first.length - 1}, (_, i) =>
                [header[i], ...Array.from({length: first.length - 1}, (_, j) => i === j ? first[i+1][1] : 0)])
          ]
       });
    
       pullData.push(
          {
             data: second.slice(1).map(([name, value]) => ({
                name,
                value
             }))
          }
       );
    
    };
    
    chartSend();
    
    // frames
    const chartUse = echarts.init(document.getElementsByClassName("chart")[0]);
    
    function chartFrameSwitch0 () {
    
       const tooltip0 = {
          show: true
       };
    
       const useDataLegend = pullDataset[0].source[0];
    
       // legend
       const legend0 = [
          {
             show: true,
             borderWidth: 2,
             data: header,
             borderColor: 'red'
          },
          {
             show: true,
             data: pullData[0].data.map(item => item.name),
             borderWidth: 2,
             borderColor: 'blue',
             left: 'center',
             top: '10%'
          }
       ];
    
       const grid0 = [
          {
             top: '30%',
             left: '5%',
             width: '38%',
             height:'30%'
          }
       ];
    
       const xAxis0 = [
          {
             gridIndex: 0,
             type: 'category',
             //data: header
          }
       ];
    
       const yAxis0 = [
          {
             gridIndex: 0,
             type: 'value'
          }
       ];
    
       const colors = ['#49a6de', '#ff7500', '#ff00ff'];
       const series0 = pullDataset[0].source.slice(1).map((item, idx) => ({
          type: 'bar',
          name: header[idx],
          color: colors[idx],
          stack: 'stack',
          encode: {
             x: 0,
             y: idx+1
          },
          xAxisIndex: 0,
          yAxisIndex: 0
       })).concat([{
          type: 'pie',
          center: ['70%', '50%'],
          data: pullData[0].data
       }]);
    
       const option = {
          dataset: pullDataset[0],
          legend: legend0, // Keep both legends in the array
          tooltip: tooltip0,
          grid: grid0,
          xAxis: xAxis0,
          yAxis: yAxis0,
          series: series0
       };
    
       chartUse.setOption(option);
    }
    
    chartFrameSwitch0();
    <div class='chart' style='height: 300px'></div>
    
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1/dist/echarts.min.js"></script>

    (or as jsFiddle).