diff --git a/ChartJs.Blazor.Tests/ChartJs.Blazor.Tests.csproj b/ChartJs.Blazor.Tests/ChartJs.Blazor.Tests.csproj index 725e4fe9..e0bc641d 100644 --- a/ChartJs.Blazor.Tests/ChartJs.Blazor.Tests.csproj +++ b/ChartJs.Blazor.Tests/ChartJs.Blazor.Tests.csproj @@ -1,20 +1,22 @@ - - netcoreapp3.1 - + net9.0 + enable + enable false - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + + all + diff --git a/global.json b/global.json index 6d6a8123..5053f424 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { "sdk": { - "version": "3.1.300", + "version": "9.0.100", "rollForward": "latestFeature", - "allowPrerelease": false + "allowPrerelease": true } -} \ No newline at end of file +} diff --git a/src/ChartJs.Blazor/Chart.razor.cs b/src/ChartJs.Blazor/Chart.razor.cs index d4991905..653deae6 100644 --- a/src/ChartJs.Blazor/Chart.razor.cs +++ b/src/ChartJs.Blazor/Chart.razor.cs @@ -10,8 +10,11 @@ namespace ChartJs.Blazor /// /// Represents a Chart.js chart. /// - public partial class Chart + public partial class Chart : IAsyncDisposable { + private bool _chartInitialized; + private bool _renderQueued; + /// /// This event is fired when the chart has been setup through interop and /// the JavaScript chart object is available. Use this callback if you need to setup @@ -24,13 +27,13 @@ public partial class Chart /// Gets the injected for the current Blazor application. /// [Inject] - protected IJSRuntime JsRuntime { get; set; } + protected IJSRuntime JsRuntime { get; set; } = default!; /// /// Gets or sets the configuration of the chart. /// [Parameter] - public ConfigBase Config { get; set; } + public ConfigBase Config { get; set; } = default!; /// /// Gets or sets the width of the canvas HTML element. @@ -47,15 +50,13 @@ public partial class Chart /// protected override async Task OnAfterRenderAsync(bool firstRender) { - if (firstRender) - { - await JsRuntime.SetupChart(Config); - await SetupCompletedCallback.InvokeAsync(this); - } - else + if (!_chartInitialized) { - await JsRuntime.UpdateChart(Config); + _chartInitialized = await TryInitializeChartAsync(); + return; } + + await JsRuntime.UpdateChart(Config); } /// @@ -66,7 +67,63 @@ protected override async Task OnAfterRenderAsync(bool firstRender) /// public Task Update() { + if (!_chartInitialized) + { + throw new InvalidOperationException("The chart has not been initialized yet."); + } + return JsRuntime.UpdateChart(Config).AsTask(); } + + /// + public ValueTask DisposeAsync() + { + _chartInitialized = false; + return ValueTask.CompletedTask; + } + + private async Task TryInitializeChartAsync() + { + try + { + await JsRuntime.SetupChart(Config); + await SetupCompletedCallback.InvokeAsync(this); + return true; + } + catch (JSDisconnectedException) + { + await QueueRenderAsync(); + return false; + } + catch (InvalidOperationException ex) when (IsInteropUnavailable(ex)) + { + await QueueRenderAsync(); + return false; + } + } + + private async Task QueueRenderAsync() + { + if (_renderQueued) + { + return; + } + + _renderQueued = true; + + await InvokeAsync(() => + { + _renderQueued = false; + StateHasChanged(); + }); + } + + private static bool IsInteropUnavailable(InvalidOperationException exception) + { + var message = exception.Message; + return message.Contains("JavaScript interop calls cannot be issued", StringComparison.OrdinalIgnoreCase) + || message.Contains("prerender", StringComparison.OrdinalIgnoreCase) + || message.Contains("JSRuntime", StringComparison.OrdinalIgnoreCase); + } } } diff --git a/src/ChartJs.Blazor/ChartJs.Blazor.csproj b/src/ChartJs.Blazor/ChartJs.Blazor.csproj index d15a0e7b..cd4139e8 100644 --- a/src/ChartJs.Blazor/ChartJs.Blazor.csproj +++ b/src/ChartJs.Blazor/ChartJs.Blazor.csproj @@ -1,10 +1,11 @@ - + - netstandard2.1 + net9.0 + enable + enable true Library - default - 3.0 + latest ChartJs.Blazor false Marius Muntean,Joelius300 @@ -40,13 +41,11 @@ - - - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + @@ -62,7 +61,7 @@ wwwroot\ChartJsBlazorInterop.js - ES2018 + ES2020 true Interop\TypeScript\*.ts