amcharts / amcharts5

The newest, fastest, and most advanced amCharts charting library for JavaScript and TypeScript apps.
Other
353 stars 94 forks source link

Change the legend value and tooltip labelText dynamically #1716

Closed prackode closed 1 month ago

prackode commented 1 month ago

I want to dynamically change the values of the legend values as well as tooltip lables. I am using stockcharts.

my valueseries and valuelgend config :

    var valueSeries = mainPanel.series.push(
      am5xy.CandlestickSeries.new(root, {
        name: tproduct,
        clustered: false,
        valueXField: "Date",
        valueYField: "Close",
        highValueYField: "High",
        lowValueYField: "Low",
        openValueYField: "Open",
        calculateAggregates: true,
        forceInactive: true,
        xAxis: dateAxis,
        yAxis: valueAxis,
        legendValueText:
          "O: [bold]{openValueY}[/] H: [bold]{highValueY}[/] L: [bold]{lowValueY}[/] C: [bold]{valueY}[/]",
        legendRangeValueText: "",
      })
    );
    var valueLegend = mainPanel.plotContainer.children.push(
      am5stock.StockLegend.new(root, {
        y: am5.percent(0),
        centerX: am5.percent(0),
        stockChart: stockChart,
        layout: am5.GridLayout.new(instance, {
          maxColumns: 1,
          fixedWidthGrid: false,
        }),
        opacity: 0.7,
      })
    );
// get the values 
// for example this: 
let currentValues = {Open : 100, High:150, Low: 190 CLose: 200, 
let convertedValue = this.changeValues(currentValues ) // this function will do the mathematical calculations kind of helper function

// now set the converted values to the legend.

And when I say conversion i want to do some mathematical calculations before showing the values in the

And whenever user hovers it will show the converted values

martynasma commented 1 month ago

You will need to create adapters for those settings.

valueSeries .adapters.add("legendValueText", function(text, target) {
  // Use target.dataItem to access original data item and construct your dynamic value
  return "dynamic " + text;
});
prackode commented 1 month ago

Yes @martynasma , I tried using the adapters but target.dataItem is giving me undefined when I hover on any candle. You can see in the below image : image (5)

the flow is :

martynasma commented 1 month ago

You're absolutely right. Sorry about that.

To access actual series data item, use target.get("tooltipDataItem"):

valueSeries .adapters.add("legendValueText", function(text, target) {
  // Use series tooltipDataItem to access original data item and construct your dynamic value
  console.log(target.get("tooltipDataItem"));
  return "dynamic " + text;
});
prackode commented 1 month ago

I was able to do for the valueSeries legend @martynasma In case of the tooltip text converstion, I am getting maximum call exceed error I am using the below code to get the data item and do the computation

let tooltip;
    if (type === "valueTooltip") {
      tooltip = am5.Tooltip.new( this.chartInstance.instance, {
        getFillFromSprite: false,
        getStrokeFromSprite: true,
        autoTextColor: false,
        pointerOrientation: "left",
        animationDuration: 0,
        labelText:
          " O: [bold]{openValueY}[/]\n H: [bold]{highValueY}[/]\n L: [bold]{lowValueY}[/]\n C: [bold]{valueY}[/]",
      });
      tooltip.adapters.add("labelText", (text, target) => {
        const tooltipDataItem = target.get("tooltipDataItem"); // this is getting undefined
       let tooltipData = target.get("labelText"); // If I add this line I get the error

          // Return the updated text with the formatted values
          return `O: [bold]${formattedValues.Open}[/]\n H: [bold]${formattedValues.High}[/]\n L: [bold]${formattedValues.Low}[/]\n C: [bold]${formattedValues.Close}[/]`;
        }

        // If tooltipDataItem is undefined, return the default text
        return text;
      });
martynasma commented 1 month ago

Well, you are requesting labelText inside an adapter for labelText which creates dead loop.

Don't do that 😉

Instead, rely on the text parameter that was passed into adapter.

prackode commented 1 month ago

Got it @martynasma I want to get the tooltip OHLC values which I am not getting :

tooltip.adapters.add("labelText", (text, target) => {
    console.log(text)
})

And similary I tried for the tooltip of the valueAxis which comes with the cursor, I want to get that value too and modify it I tried this :

valueSeries .adapters.add("tooltip", function(text, target) {
  console.log(target.get("tooltipDataItem"));
  return "dynamic " + text;
});
martynasma commented 1 month ago

E.g.:

valueSeries.adapters.add("legendValueText", function(text, target) {
  var dataItem = target.get("tooltipDataItem")
  if (dataItem) {
    return dataItem.get("highValueY");
  }
  return text;
});

For axis tooltip, you will need to target the series:

dateAxis.get("tooltip").adapters.add("labelText", function(text, target) {
  var dataItem = valueSeries.get("tooltipDataItem")
  if (dataItem) {
    return dataItem.get("highValueY");
  }
  return text;
});

Example: https://codepen.io/team/amcharts/pen/qBzgzWa/a4d0a579534c75cf2bc6538fef644dce?editors=0010

prackode commented 1 month ago

Thanks @martynasma for giving the codepen. You can see in the image the tooltip of the valueAxis is showing the value as 586.73 whereas in the console it is showing the 593.25, image (6)

I basically want to get and update values of these 2 places only :

So for valueSeries tooltip I tried this :

valueAxis.get("tooltip").adapters.add("labelText", function(text, target) {
  var dataItem = valueSeries.get("tooltipDataItem")
console.log(dataItem?.get("highValueY"))
  return text;
});

And for the Tooltip box I tried this :


 tooltip.adapters.add("labelText", (text, target) => {
    console.log(text)
})

Both of them didn't gave me the values of the current state.

martynasma commented 1 month ago
series.get("tooltip").label.adapters.add("labelText", function(text, target) {
  var dataItem = target.dataItem;
  console.log(dataItem?.get("highValueY"))
  return text;
});
valueAxis.get("tooltip").adapters.add("labelText", function(text, target) {
  var dataItem = valueSeries.get("tooltipDataItem")
  if (dataItem) {
    return dataItem.get("highValueY");
  }
  return text;
});
prackode commented 1 month ago

@martynasma I want to get the cursor tooltip which being hovered over the valueAxis. specified that we can get the value of the tooltip like this :

 valueAxis.get("tooltip").label.adapters.add("labelText", function(text, target) {
  var dataItem = target.dataItem;
  console.log(dataItem?.get("highValueY"))
  return text;
});

But you can see in the video that I am getting the high value of the candle, I want that cursor tooltip value which is changing when we are moving the cursor. Only the valueAxis tooltip (box)

https://github.com/user-attachments/assets/c9c1813c-787d-4477-94bb-6699374be792

martynasma commented 1 month ago

You want to display "high value" of a hovered candle in a value axis tooltip?

prackode commented 1 month ago

No @martynasma Whatever the value is shown on the cursor tooltip which is on valueAxis, when I hover all over the chart weather I am hovering over the candle or not. As you can see in the video that, when I move my cursor upwards the cursor tooltip is showing the values

martynasma commented 1 month ago

I'm sorry I'm still not getting what you need.

Value axis tooltips displays whatever value corresponds to the position of the cursor.

You need it to snap to candle? Something else?

prackode commented 1 month ago

@martynasma I want to get this value : image

How can I get the what value is showing on this tooltip? What's the property/field is shown.

So for example in the image it is showing 376.90 , I want to get that.

martynasma commented 1 month ago
var y = ev.target.getPrivate("positionY");
var valueY = yAxis.positionToValue(y);
prackode commented 1 month ago

Tried this @martynasma but it is not giving the exact value which is being shown on the cursor tooltip. image (8)

As you can see in the below image, the cursor tooltip on valueAxis is showing the value as 393.59 but on console it is not printing.

martynasma commented 1 month ago

You probably have snapping of cursor to candles enabled.

In such case the value will be shown as physical location of cursor.

cursor.events.on("cursormoved", function(ev) {
  var y = ev.target.getPrivate("positionY");
  var valueY = valueAxis.positionToValue(y);
  console.log(valueY)
})

If you want a value cursor is snapped to, then you will need to use tooltipDataItem.

cursor.events.on("cursormoved", function(ev) {
  var dataItem = valueSeries.get("tooltipDataItem")
  valueY = dataItem ? dataItem.get("valueY") : 0;
  console.log(valueY)
})
prackode commented 1 month ago

It still didn't worked @martynasma I am providing you the codepen for the better testing and understanding

https://codepen.io/Prakhar-Agarwal-the-styleful/pen/gONVRpb?editors=1011

I only want to get the cursor tooltip showing price that's it . image

What ever the value this tooltip is showing I want to get it and do some mathematical operation and then set it. So for example the below image show the cursor tooltip price value as 392.59 , and consider hypothetically I want to multiply that value by 2 and then show it.

image

martynasma commented 1 month ago
valueAxis.get("tooltip").label.adapters.add("text", function(text, target) {
  var dataItem = valueSeries.get("tooltipDataItem")
  var valueY = dataItem ? dataItem.get("valueY") : 0;
  return valueY * 2 + "";
})
prackode commented 1 month ago

This worked for me by the way @martynasma


cursor.events.on("cursormoved", function(ev) {
  var y = ev.target.getPrivate("positionY");
  var valueY = valueAxis.positionToValue(y);
  var yMax = valueAxis.positionToValue(1);
  var yMin = valueAxis.positionToValue(0);
  console.log(yMax + yMin - valueY)
})
prackode commented 1 month ago

But there issue that when we put this code on the Live updating chart, I am continously getting the values even though I am not moving the cursor @martynasma

Try using the above code in this https://www.amcharts.com/demos/live-stock-data/

And use the cursor once then don't move it, then too console is printing. I don't want that, I only want to console the values when I am moving the cursor only

martynasma commented 1 month ago

cursormoved will not kick in if cursor is not over the chart. If it's over the chart, it will kick in whenever something updates.

prackode commented 1 month ago

What do now ? The cursor is on the chart, but I am not moving it & it is getting live update.

As the name suggest cursormoved , so it should work when the cursor moved. Isn't it? @martynasma Or is there any other event/hack which can help

martynasma commented 1 month ago

It's not a huge problem. You can just verify if the positionY is the same as previous and ignore event invocation.