apexcharts / Blazor-ApexCharts

A blazor wrapper for ApexCharts.js
https://apexcharts.github.io/Blazor-ApexCharts
MIT License
788 stars 91 forks source link

ApexPointSeries visualization and updating issues #359

Closed sv-ed closed 8 months ago

sv-ed commented 9 months ago

I'm using Blazor-ApexCharts nuget 2.2.0 in a blazor .net 8 app.

I've 5 charts in the page and some dropdown and datetimepicker to filter data. Each chart have dedicated options where I customize the toolbar, min and max values of YAxis, YAxis annotations and a dozen of Discrete Markers. When I change the page filters, the query gets updated and the data binded to the chart also changes. The data obtained from the query is a List<Data> and the data returned is correctly filtered and can be empty (if empty the charts are not included in the page). The binded data of the class Data are the prop Label (string) and Value (double)

Example of the page:

@rendermode InteractiveServer
@attribute [StreamRendering]

@*... filters ...*@

if (_data.Count == 0)
    {
        <p class="text-center mt-5">No data found.</p>
    }
    else
    {
        <div class="row mt-2">
            <ApexChart TItem="Data" Title="Items" Options="@_options" @ref="_chart">
                <ApexPointSeries TItem="Data"
                                 Items="_data"
                                 Name="Items"
                                 SeriesType="SeriesType.Line"
                                 XValue="@(e => e.Label)"
                                 YValue="@(e => (decimal?)e.Value)" />
            </ApexChart>
        </div>
        @*... other 4 charts ...*@
    }

ISSUES

The first issue is that the chart is not updated always. It can happen that the options are updated but not the series and the charts ends up with updated markers on an invalid series line. Here is how I update the charts, this operation is done for all the 5 charts:

await _chart.UpdateSeriesAsync();
await _chart.UpdateOptionsAsync(true, true, true);
await _chart.RenderAsync();

The second issue is that occasionally throws an exception in RenderChartAsync() and so the chart does not get updated.

Exception Message: Object reference not set to an instance of an object.
Exception StackTrace: at ApexCharts.ApexChart`1.<RenderChartAsync>d__175.MoveNext() in ApexCharts\ApexChart.cs:line 621
joadan commented 9 months ago

Hello,

Not sure why do you run all methods here?

await _chart.UpdateSeriesAsync();
await _chart.UpdateOptionsAsync(true, true, true);
await _chart.RenderAsync();

what do you want to do?

sv-ed commented 9 months ago

I tried all of those cause I was trying to make it works. Initially I only used UpdateSeriesAsync and RenderAsync but didn't work either.

My goal is that, when a filter is changed, the charts update but I'm encountering the issues I described earlier,

sv-ed commented 9 months ago

Update: Seems like I found a workaround for the first issue. I manually clear and then add the series everytime I need to update the data and then call UpdateSeriesAsync() Here's a snippet:

 _chart.Series.Clear();
 ApexPointSeries<Data> series = new ApexPointSeries<Data>()
 {
     Chart = _chart,
     Items = _data,
     Name = "Items",
     SeriesType = SeriesType.Line,
     XValue = (e => e.Label),
     YValue = (e => (decimal?)e.Value),
 };
 _chart.Series.Add(series);

Make sure to specify Chart in serie otherwise it crashes. This seems to work even tho is quite annoying, specially if you have multiple charts. But now I'm able to update the series consistently.

With this workaround tho, I'm always having the excpetion I described in the issue when calling RenderAsync():

Exception Message: Object reference not set to an instance of an object.
Exception StackTrace: at ApexCharts.ApexChart`1.<RenderChartAsync>d__175.MoveNext() in ApexCharts\ApexChart.cs:line 621

Without calling RenderAsync() it updates the series with UpdateSeriesAsync() but the markers and other options are not updated. So now instead of having the options updated but not the series I've the series updated but not the options.

@joadan do you have any ideas what is causing this exception? Am I missing something?

joadan commented 9 months ago

hello,

My general advice is to start simple..

Start with one chart gradually add more advanced stuff, check the samples. like https://apexcharts.github.io/Blazor-ApexCharts/features/dynamic-series

If you still have issues try to narrow it down as simple as possible if you can provide a repo with the issue is reproduced it's much easier for us to help.

sv-ed commented 9 months ago

I invited you to a very simple repo with a single chart with a single series where the exception issue is present. I kept the RenderAsync() because without it the markers and other options aren't rendered (in the original project of course, not in the example repo).

Please take a look at it, thanks in advance.

joadan commented 9 months ago

Hello,

I have not received any invite..

sv-ed commented 9 months ago

@joadan Here is the direct link then: https://github.com/sv-ed/TestApexCharts/invitations

btw, the issue seems to be that JSHandler in RenderChartAsync() is null. Checking if it's null and returning before InvokeVoidJsAsync fixes it. I didn't do any PR yet, waiting for your feedback on the repo I shared where this issue is always present.

joadan commented 9 months ago

btw, the issue seems to be that JSHandler in RenderChartAsync() is null. Checking if it's null and returning before InvokeVoidJsAsync fixes it. I didn't do any PR yet, waiting for your feedback on the repo I shared where this issue is always present.

Have you checked the latest version? I have done some work in this area.

sv-ed commented 9 months ago

I tried latest nuget (2.2.0) and cloned repo (master branch) and encoutered the issue in both.

joadan commented 9 months ago

Hello,

I have updated the repo, please have a look. If you need to dynamically add/remove series please check the sample at: https://apexcharts.github.io/Blazor-ApexCharts/features/dynamic-series.

Do not directly add remove series in the chart options, that will not work.

sv-ed commented 9 months ago

Hi @joadan, thanks for looking into it. In the main project I'm working on I managed to solve the series not automatically updating, by manually calling StateHasChanged() before rendering the chart. So now I just need to call await _chart.RenderAsync(); and it updates correctly without having to manually add the series.

I'm still encountering the null reference issue (JSHandler in RenderChartAsync() is null) when, in the razor page, I conditionally hide the chart and then draw it back. I've modified the repo to add this example

joadan commented 9 months ago

I'm not sure why you are keeping to add

 await _chart.UpdateSeriesAsync();
 await _chart.RenderAsync();

Running two methods like this just doesn't make sense. Have you seen one example in the documentation doing this? In your test repo I did show you to use UpdateOptionsAsync, that is the method to use.

sv-ed commented 9 months ago

I saw the PR you made, I'll use UpdateOptionsAsync only then, thanks What is the ideal usecase of RenderAsync?

joadan commented 9 months ago

RenderAsync will completely re-render the component that includes the recreate the underlying javascript object. RendersAsync is used internally the first time the chart is rendered.

It could be used to do a complete refresh of the char, reset all state.

Please close the issue if there are no more questions. Also don't forget to star the repo if you like it.

sv-ed commented 9 months ago

Thank you for your response. I'm having some troubles with label visualization on the X axis where, if the string is long, it gets "cut" at the beginnning and at the bottom (if there are enough labels to arrange them vertically). Example of labels cut at the beginning: image

Example of labels cut at the bottom: image

joadan commented 9 months ago

Hi, Have you read, https://apexcharts.com/docs/multiline-text-and-line-breaks-in-axes-labels/ Questions like this is probably better of in the apexcharts.js repo. Thank you

sv-ed commented 8 months ago

Thank you for the suggestion, I dug a little deeper and found a reasonable solution. By default the labels MaxHeight is set to 120. (https://apexcharts.com/docs/options/xaxis/#maxheight) I've increased it to make longer labels fit and now it works fine.

Thank you for your help, I'll closing the issue