apexcharts / Blazor-ApexCharts

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

System.ArgumentNullException when using OnDataPointSelection with Options #478

Closed mars888 closed 2 months ago

mars888 commented 3 months ago

Hi there!

We have recently run into a strange behavior after upgrading a Blazor WebAssembly application to .NET 8. We have a page with a number of bar graphs displaying a limited amount of data. The first load of this page is fine and the graph displays without issues. The first click on a data entry works without issues as well, but any subsequent clicks produce the following error in the browser console:

blazor.webassembly.js?:1 Uncaught (in promise) Error: System.ArgumentNullException: Value cannot be null. (Parameter 'source')
   at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
   at System.Linq.Enumerable.ElementAt[Series`1](IEnumerable`1 source, Int32 index)
   at ApexCharts.Internal.JSHandler`1[[OurNamespace.DataEntry, OurNamespace, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].JSDataPointSelected(JSDataPointSelection selectedDataPoints)
   at System.Object.InvokeStub_JSHandler`1.JSDataPointSelected(Object , Span`1 )
   at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
--- End of stack trace from previous location ---
   at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.InvokeSynchronously(JSRuntime jsRuntime, DotNetInvocationInfo& callInfo, IDotNetObjectReference objectReference, String argsJson)
   at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.BeginInvokeDotNet(JSRuntime jsRuntime, DotNetInvocationInfo invocationInfo, String argsJson)
    at b.endInvokeDotNetFromJS (http://localhost:5010/_framework/blazor.webassembly.js:1:3136)
    at Object.gn [as endInvokeDotNetFromJS] (http://localhost:5010/_framework/blazor.webassembly.js:1:58943)
    at http://localhost:5010/_framework/dotnet.runtime.8.0.5.embnw6otr2.js:3:178364
    at Tl (http://localhost:5010/_framework/dotnet.runtime.8.0.5.embnw6otr2.js:3:179198)
    at wasm://wasm/00b2193a:wasm-function[349]:0x1fab4
    at wasm://wasm/00b2193a:wasm-function[245]:0x1bf5b
    at wasm://wasm/00b2193a:wasm-function[238]:0xf142
    at wasm://wasm/00b2193a:wasm-function[272]:0x1d179
    at wasm://wasm/00b2193a:wasm-function[3185]:0xe8673
    at wasm://wasm/00b2193a:wasm-function[2505]:0xbe3c9

I've reduced the code in question to this minimal reproducible sample that still produces this error:

<ApexChart
    TItem="DataEntry"
    Options="@(new ApexChartOptions<DataEntry>())"
    OnDataPointSelection="@(selectedData => { })">
    <ApexPointSeries
        TItem="DataEntry"
        Items="dataEntries"
        XValue="@(m => m.XValue)"
        YValue="@(m => (decimal)m.YValue)"
        SeriesType="SeriesType.Bar"/>
</ApexChart>

Of note here is that the errors go away when either the Options or the OnDataPointSelection attributes are removed. I've spent about a day and a half on this, but I can't seem to figure out where this is coming from.

This is using Blazor-ApexCharts version 3.3.0. Any ideas or insights on this behavior are greatly appreciated!

mars888 commented 2 months ago

After a bit of tracing, it seems that the issue is that the Options attribute on ApexChart is set multiple times by Blazor (each time with a new ApexChartOptions<_> instance).

This can sometimes happen after PrepareChart has been called (I assume this happens when the interface updates/renders and the order here is dependent on how the user interacts with the page or maybe just timing). As the options are cleanly created in this case, the Series property will be null as SetSeries will not have been called. This in turn causes the line var series = ChartReference.Options.Series.ElementAt(selectedDataPoints.SeriesIndex) in JsHandler.JSDataPointEnter/JsHandler.JSDataPointSelected to explode with a System.ArgumentNullException.

Is creating new options for the Options attribute something that is expected and supported in such a case?

joadan commented 2 months ago

Hi In your code you get a new ApexchartsOptions object each time the Options Parameter is read. You should use an variable and new it up once.

Please check the samples like https://apexcharts.github.io/Blazor-ApexCharts/features/axis

mars888 commented 2 months ago

Ok, we have some complex logic where some of the graph parameters and incoming data could change, and we combine these updates in a functional (immutable) way. But I understand why this is something that would not be supported and we can certainly find ways to work around this. Thank you for your response!