Opened

Charts not working in production environment

Convotis_Innovations commented edited

Hello!
I'm running into an issue when setting the options of a chart.

The code below works as intended on a local server with local database, but if I switch to an Azure hosted (slower) test database, the exception below is thrown.

When the setOptions call is removed, the exception is no longer thrown.

(I have used an empty BarChartsOptions object to illustrate that it doesn’t seem dependent on the actual options set.)

I already have tried to delay each step of rendering the chart to avoid any possible race conditions, but it still occurs.

Help is highly appreciated. Thank you!

<BarChart @ref="chart" TItem="int"/>

@code {
    private BarChart<int> chart;
    private List<string> labels;
    private List<int> dataPoints;

	protected override async Task OnAfterRenderAsync(bool firstRender)
	{
	    if (firstRender)
	    {
	        await this.DrawChart(this.labels, this.dataPoints);
	    }
	}

	private async Task DrawChart(List<string> labels, List<int> dataPoints)
	{
	    var dataset = new BarChartDataset<int>
	                  {
	                      Data = dataPoints,
	                  };

	    await this.chart.Clear();
	    await this.chart.SetOptions(new BarChartOptions());
	    await this.chart.AddLabelsDatasetsAndUpdate(labels, dataset);
	}

}
> warn: Microsoft.AspNetCore.Components.Server.Circuits.RemoteRenderer[100]
>       Unhandled exception rendering component: Cannot set property aspectRatio of #<dn> which has only a getter
> TypeError: Cannot set property aspectRatio of #<dn> which has only a getter
>     at Module.setOptions (https://localhost:5001/_content/Blazorise.Charts/charts.js?v=1.0.5.0:177:31)
>     at https://localhost:5001/_framework/blazor.server.js:1:3501
>     at new Promise (<anonymous>)
>     at kt.beginInvokeJSFromDotNet (https://localhost:5001/_framework/blazor.server.js:1:3475)
>     at https://localhost:5001/_framework/blazor.server.js:1:72001
>     at Array.forEach (<anonymous>)
>     at kt._invokeClientMethod (https://localhost:5001/_framework/blazor.server.js:1:71987)
>     at kt._processIncomingData (https://localhost:5001/_framework/blazor.server.js:1:70029)
>     at connection.onreceive (https://localhost:5001/_framework/blazor.server.js:1:64432)
>     at o.onmessage (https://localhost:5001/_framework/blazor.server.js:1:48766)
>       Microsoft.JSInterop.JSException: Cannot set property aspectRatio of #<dn> which has only a getter
> TypeError: Cannot set property aspectRatio of #<dn> which has only a getter
>     at Module.setOptions (https://localhost:5001/_content/Blazorise.Charts/charts.js?v=1.0.5.0:177:31)
>     at https://localhost:5001/_framework/blazor.server.js:1:3501
>     at new Promise (<anonymous>)
>     at kt.beginInvokeJSFromDotNet (https://localhost:5001/_framework/blazor.server.js:1:3475)
>     at https://localhost:5001/_framework/blazor.server.js:1:72001
>     at Array.forEach (<anonymous>)
>     at kt._invokeClientMethod (https://localhost:5001/_framework/blazor.server.js:1:71987)
>     at kt._processIncomingData (https://localhost:5001/_framework/blazor.server.js:1:70029)
>     at connection.onreceive (https://localhost:5001/_framework/blazor.server.js:1:64432)
>     at o.onmessage (https://localhost:5001/_framework/blazor.server.js:1:48766)
>          at Microsoft.JSInterop.JSRuntime.InvokeAsync[TValue](Int64 targetInstanceId, String identifier, Object[] args)
>          at Microsoft.JSInterop.JSObjectReferenceExtensions.InvokeVoidAsync(IJSObjectReference jsObjectReference, String identifier, Object[] args)
>          at Blazorise.Charts.JSChartModule.SetOptions[TOptions](String canvasId, TOptions options, String optionsJsonString, Object optionsObject)
>          at Blazorise.Charts.BaseChart`4.SetOptionsObject(Object optionsObject)
>          at Parking.Shared.UsageHistoryChartUser.DrawChart(List`1 labels, List`1 dataPoints) in UsageHistoryChartUser.razor.cs:line 88
>          at Parking.Shared.UsageHistoryChartUser.OnAfterRenderAsync(Boolean firstRender) in UsageHistoryChartUser.razor.cs:line 62
>          at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
> fail: Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost[111]
>       Unhandled exception in circuit 'wyKmQh-WTV5KNbqsFMZMnj9I6vY_2F9308qpezQdO4E'.
>       Microsoft.JSInterop.JSException: Cannot set property aspectRatio of #<dn> which has only a getter
> TypeError: Cannot set property aspectRatio of #<dn> which has only a getter
>     at Module.setOptions (https://localhost:5001/_content/Blazorise.Charts/charts.js?v=1.0.5.0:177:31)
>     at https://localhost:5001/_framework/blazor.server.js:1:3501
>     at new Promise (<anonymous>)
>     at kt.beginInvokeJSFromDotNet (https://localhost:5001/_framework/blazor.server.js:1:3475)
>     at https://localhost:5001/_framework/blazor.server.js:1:72001
>     at Array.forEach (<anonymous>)
>     at kt._invokeClientMethod (https://localhost:5001/_framework/blazor.server.js:1:71987)
>     at kt._processIncomingData (https://localhost:5001/_framework/blazor.server.js:1:70029)
>     at connection.onreceive (https://localhost:5001/_framework/blazor.server.js:1:64432)
>     at o.onmessage (https://localhost:5001/_framework/blazor.server.js:1:48766)
>          at Microsoft.JSInterop.JSRuntime.InvokeAsync[TValue](Int64 targetInstanceId, String identifier, Object[] args)
>          at Microsoft.JSInterop.JSObjectReferenceExtensions.InvokeVoidAsync(IJSObjectReference jsObjectReference, String identifier, Object[] args)
>          at Blazorise.Charts.JSChartModule.SetOptions[TOptions](String canvasId, TOptions options, String optionsJsonString, Object optionsObject)
>          at Blazorise.Charts.BaseChart`4.SetOptionsObject(Object optionsObject)
>          at Parking.Shared.UsageHistoryChartUser.DrawChart(List`1 labels, List`1 dataPoints) in UsageHistoryChartUser.razor.cs:line 88
>          at Parking.Shared.UsageHistoryChartUser.OnAfterRenderAsync(Boolean firstRender) in UsageHistoryChartUser.razor.cs:line 62
>          at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
mladenmacanovic commented

We would have to investigate it further, but in the meantime can you try setting the options through the parameter instead of through the method call?

<BarChart @ref="chart" TItem="int" Options="@chartOptions" />

@code{
	ChartOptions chartOptions = new()
	{
		// options
	};
}
Convotis_Innovations commented

Thank you for your quick answer!
Interestingly if I set it through the parameter, it works.
Only the Tooltip options seem to be ignored.

Here is my full options object:

private BarChartOptions GetOptions()
{
var options = new BarChartOptions();

    options.AspectRatio = 1.5f;

    options.Scales = new ChartScales();

    options.Scales.Y = new ChartAxis();
    options.Scales.Y.Display = false;

    options.Scales.X = new ChartAxis();
    options.Scales.X.Grid = new ChartAxisGridLine();
    options.Scales.X.Grid.Display = false;

    options.Plugins = new ChartPlugins();
    options.Plugins.Legend = new ChartLegend();
    options.Plugins.Legend.Display = false;

    options.Plugins.Tooltips = new ChartTooltips();
    options.Plugins.Tooltips.Enabled = false;

return options;
}
Convotis_Innovations commented

Hello!
Has there been any progress?

While setting the options as a parameter works, changing the options after the first initialization is not possible with this method.

mladenmacanovic commented

I'm trying with the latest v1.0.6 but I cannot reproduce the error. Can you try updating your project?

Here is my code

<BarChart @ref="chart" TItem="int" />

@code {
    private BarChart<int> chart;
    private List<string> labels = new List<string> { "Red", "Blue", "Yellow", "Green", "Purple", "Orange" };
    private List<int> dataPoints = new List<int> { 10, 15, 12, 5, 9, 16 };
    private List<string> backgroundColors = new() { ChartColor.FromRgba( 255, 99, 132, 0.2f ), ChartColor.FromRgba( 54, 162, 235, 0.2f ), ChartColor.FromRgba( 255, 206, 86, 0.2f ), ChartColor.FromRgba( 75, 192, 192, 0.2f ), ChartColor.FromRgba( 153, 102, 255, 0.2f ), ChartColor.FromRgba( 255, 159, 64, 0.2f ) };
    private List<string> borderColors = new() { ChartColor.FromRgba( 255, 99, 132, 1f ), ChartColor.FromRgba( 54, 162, 235, 1f ), ChartColor.FromRgba( 255, 206, 86, 1f ), ChartColor.FromRgba( 75, 192, 192, 1f ), ChartColor.FromRgba( 153, 102, 255, 1f ), ChartColor.FromRgba( 255, 159, 64, 1f ) };

    protected override async Task OnAfterRenderAsync( bool firstRender )
    {
        if ( firstRender )
        {
            await this.DrawChart( this.labels, this.dataPoints );
        }
    }

    private async Task DrawChart( List<string> labels, List<int> dataPoints )
    {
        var dataset = new BarChartDataset<int>
            {
                Label = "# of randoms",
                Data = dataPoints,
                BackgroundColor = backgroundColors,
                BorderColor = borderColors,
                BorderWidth = 1
            };

        await this.chart.Clear();
        await this.chart.SetOptions( GetOptions() );
        await this.chart.AddLabelsDatasetsAndUpdate( labels, dataset );
    }

    private BarChartOptions GetOptions()
    {
        var options = new BarChartOptions();

        options.AspectRatio = 1.5f;

        options.Scales = new ChartScales();

        options.Scales.Y = new ChartAxis();
        options.Scales.Y.Display = false;

        options.Scales.X = new ChartAxis();
        options.Scales.X.Grid = new ChartAxisGridLine();
        options.Scales.X.Grid.Display = false;

        options.Plugins = new ChartPlugins();
        options.Plugins.Legend = new ChartLegend();
        options.Plugins.Legend.Display = false;

        options.Plugins.Tooltips = new ChartTooltips();
        options.Plugins.Tooltips.Enabled = false;

        return options;
    }
}
Convotis_Innovations commented

Hello!
In the meantime I also updated the project to 1.0.6. Now the error seems to be fixed. Thank you and sorry for raising an issue that has been fixed already.

nathanh commented

I know this is closed but I have a similar issue.

I have been trying to find away to change the options after the initial render and it does not seem to work.

Calling SetOptions() on my Line Chart not only does not seem to update the options but then seems to block the chart data actually being rendered.

Using the same data and setup (and on 1.0.6) the following renders an empty chart.

await chartHistory.AddDataSet(dataset);
await chartHistory.AddLabels(labels);
await chartHistory.SetOptions(chartOptions);
await chartHistory.Update();

This renders perfectly fine as expected.

await chartHistory.AddDataSet(dataset);
await chartHistory.AddLabels(labels);
//  await chartHistory.SetOptions(chartOptions);
await chartHistory.Update();

The code block is run from a button press after the page/control has been rendered.

Convotis_Innovations commented edited

Hello!
I reopened the issue since I think it is not actually fixed, but depends on a race condition, that may or many not resolve based on the runtime performance.
The reason I have that suspicion, is that adding in await Task.Delay(1000); into your sample code now causes a different exception to be raised. Removing the await this.chart.SetOptions( GetOptions() ); solves this even with the delay added.

I noticed the "content/Blazorise.Charts/charts.js?v=1.0.6.0" file being loaded rather late while loading the page, and that some of the Charts interactions seem to depend on that file. Is it possible that this might be the source of the race condition? I'm not a 100% sure though.

Altered code:

        await this.chart.Clear();
        await Task.Delay(1000);
        await this.chart.SetOptions( GetOptions() );
        await this.chart.AddLabelsDatasetsAndUpdate( labels, dataset );

New Exception:

warn: Microsoft.AspNetCore.Components.Server.Circuits.RemoteRenderer[100]
      Unhandled exception rendering component: Property accessor 'IndexedValues' on object 'Blazorise.Charts.IndexableOption`1[[System.Object, System.Private.CoreLib, Version=6.0.0.0, Cult
ure=neutral, PublicKeyToken=7cec85d7bea7798e]]' threw the following exception:'This instance represents a single value. The indexed values are not available.'
      System.Reflection.TargetInvocationException: Property accessor 'IndexedValues' on object 'Blazorise.Charts.IndexableOption`1[[System.Object, System.Private.CoreLib, Version=6.0.0.0,
Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]' threw the following exception:'This instance represents a single value. The indexed values are not available.'
       ---> System.InvalidOperationException: This instance represents a single value. The indexed values are not available.
         at Blazorise.Charts.IndexableOption`1.get_IndexedValues()
         --- End of inner exception stack trace ---
         at System.ComponentModel.ReflectPropertyDescriptor.GetValue(Object component)
         at Blazorise.Utilities.Converters.ToDictionary(Object source, Boolean addEmptyObjects, Boolean forceCamelCase)
         at Blazorise.Utilities.Converters.<ToDictionary>g__ProcessValue|1_0(Object value, Boolean emitDefaultValue, <>c__DisplayClass1_0& )
         at Blazorise.Utilities.Converters.ToDictionary(Object source, Boolean addEmptyObjects, Boolean forceCamelCase)
         at Blazorise.Utilities.Converters.<ToDictionary>g__ProcessValue|1_0(Object value, Boolean emitDefaultValue, <>c__DisplayClass1_0& )
         at Blazorise.Utilities.Converters.ToDictionary(Object source, Boolean addEmptyObjects, Boolean forceCamelCase)
         at Blazorise.Utilities.Converters.<ToDictionary>g__ProcessValue|1_0(Object value, Boolean emitDefaultValue, <>c__DisplayClass1_0& )
         at Blazorise.Utilities.Converters.ToDictionary(Object source, Boolean addEmptyObjects, Boolean forceCamelCase)
         at Blazorise.Charts.BaseChart`4.SetOptions(TOptions options)
         at Carage.Shared.UsageHistoryChartUser.DrawChart(List`1 labels, List`1 dataPoints) in C:\Users\user\projects\carage\src\Carage\Shared\UsageHistoryChartUser.razor.cs:line 7
2
         at Carage.Shared.UsageHistoryChartUser.OnAfterRenderAsync(Boolean firstRender) in C:\Users\user\projects\carage\src\Carage\Shared\UsageHistoryChartUser.razor.cs:line 55
         at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
fail: Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost[111]
      Unhandled exception in circuit 'Q59X4x_eO9mL6eBYxKmJzy3m3mrD6f8BXZ7LGPDCLDM'.
      System.Reflection.TargetInvocationException: Property accessor 'IndexedValues' on object 'Blazorise.Charts.IndexableOption`1[[System.Object, System.Private.CoreLib, Version=6.0.0.0,
Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]' threw the following exception:'This instance represents a single value. The indexed values are not available.'
       ---> System.InvalidOperationException: This instance represents a single value. The indexed values are not available.
         at Blazorise.Charts.IndexableOption`1.get_IndexedValues()
         --- End of inner exception stack trace ---
         at System.ComponentModel.ReflectPropertyDescriptor.GetValue(Object component)
         at Blazorise.Utilities.Converters.ToDictionary(Object source, Boolean addEmptyObjects, Boolean forceCamelCase)
         at Blazorise.Utilities.Converters.<ToDictionary>g__ProcessValue|1_0(Object value, Boolean emitDefaultValue, <>c__DisplayClass1_0& )
         at Blazorise.Utilities.Converters.ToDictionary(Object source, Boolean addEmptyObjects, Boolean forceCamelCase)
         at Blazorise.Utilities.Converters.<ToDictionary>g__ProcessValue|1_0(Object value, Boolean emitDefaultValue, <>c__DisplayClass1_0& )
         at Blazorise.Utilities.Converters.ToDictionary(Object source, Boolean addEmptyObjects, Boolean forceCamelCase)
         at Blazorise.Utilities.Converters.<ToDictionary>g__ProcessValue|1_0(Object value, Boolean emitDefaultValue, <>c__DisplayClass1_0& )
         at Blazorise.Utilities.Converters.ToDictionary(Object source, Boolean addEmptyObjects, Boolean forceCamelCase)
         at Blazorise.Charts.BaseChart`4.SetOptions(TOptions options)
         at Carage.Shared.UsageHistoryChartUser.DrawChart(List`1 labels, List`1 dataPoints) in C:\Users\user\projects\carage\src\Carage\Shared\UsageHistoryChartUser.razor.cs:line 7
2
         at Carage.Shared.UsageHistoryChartUser.OnAfterRenderAsync(Boolean firstRender) in C:\Users\user\projects\carage\src\Carage\Shared\UsageHistoryChartUser.razor.cs:line 55
         at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)


mladenmacanovic commented

The chart JS is loaded dynamically as a JS module. That's why it takes longer. But even then it should be fine because chart options and data should be applied only after everything was loaded and ready to be rendered. I will need to investigate and see why it breaks in your case.

nathanh commented

It's not just their case, I get the exact same exception when using SetOptions().

The only workaround I found for setting the options after an initial render was to have the chart control in an IF statement that only renders when I set a parameter.

@if (ShowChart) {  
        <LineChart @ref="ChartHistory" TItem="int" Options="@ChartHistoryOptions" />   
}

So if I want to change the options in ChartHistoryOptions, I basically have to do the following:

ChartHistoryOptions = new { .... }
ShowChart = false;
StateHasChanged();
ShowChart = true;
StateHasChanged();

Which then allows the chart to re-render with the new options. However, this then has the issue that if there are multiple objects/components trying to render and call StateHasChanged(), .NET will wait a buffer a number of the StateHasChanged() calls and wait to do a single actual re-render at the end, meaning then the chart wont render with the new options.

Want to comment on this issue? Log in and write your comment.
Asignee
No assignee
Labels
No Labels