ChartJs.Blazor

repository·master·Indexed 20 days ago

https://github.com/mariusmuntean/chartjs.blazor

A Blazor library providing a C# wrapper around the Chart.js JavaScript library for creating interactive charts in Blazor Server and Blazor WebAssembly projects. Distributed via the ChartJs.Blazor.Fork NuGet package, it includes a ChartJsInterop layer to manage chart initialization, updates, and callback hooks for events like onClick and onHover.

Tokens
2.2K
Snippets
5
Records
8
Agent score
22%

What's inside ChartJs.Blazor

  1. Install ChartJs.Blazor.Fork via NuGet

    master

    Due to current availability, the 2.0 release is distributed via the ChartJs.Blazor.Fork NuGet package rather than the original ChartJs.Blazor package. You can install it using the NuGet Package Manager in your IDE or via the .NET CLI.

    dotnet add package ChartJs.Blazor.Fork
  2. Configure static assets for Chart.js

    master

    To enable chart rendering, you must include the Chart.js library and the library's interop script in your project's entry HTML file.

    • For Server-side Blazor: Add these to _Host.cshtml.
    • For Client-side (Wasm) Blazor: Add these to index.html.

    Place these tags inside the <body> tag after the _framework reference.

    If you are using a TimeAxis, you must also include Moment.js before the Chart.js script.

    <!-- If using TimeAxis, include Moment.js first -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
    
    <!-- Chart.js core -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js"></script>
    
    <!-- Glue between Blazor and Chart.js -->
    <script src="_content/ChartJs.Blazor.Fork/ChartJsBlazorInterop.js"></script>
  3. Import ChartJs.Blazor namespaces

    master

    To use the library components and configuration classes, add the following to your _Imports.razor file:

    @using ChartJs.Blazor;

    For more specific functionality, you may also need to import:

    • ChartJs.Blazor.Common
    • ChartJs.Blazor.Common.Axes
    • ChartJs.Blazor.Common.Axes.Ticks
    • ChartJs.Blazor.Common.Enums
    • ChartJs.Blazor.Common.Handlers
    • ChartJs.Blazor.Common.Time
    • ChartJs.Blazor.Util
    • ChartJs.Blazor.Interop
    • Specific chart namespaces, e.g., ChartJs.Blazor.PieChart
  4. How ChartJsInterop handles callbacks and delegates

    master

    The interop layer allows Chart.js events (like onClick, onHover, or legend filters) to trigger code in Blazor. It supports two types of handlers via the IMethodHandler interface:

    1. JavaScript Functions: If methodName is provided in a format like Namespace.FunctionName, the interop looks for window['Namespace']['FunctionName'].
    2. DotNet Delegates: If the handler contains a handlerReference (DotNetObjectReference), the interop invokes the specified C# method.

    Important Behaviors:

    • Async vs Sync: C# delegates that do not return a value are invoked using invokeMethodAsync.
    • Server-Side Blazor Limitation: C# delegates that do return a value (synchronous interop) are only supported on the Client-side (WebAssembly). On Server-side Blazor, these will fall back to the default Chart.js handler because the server-side dispatcher does not support synchronous interop calls.
    • Argument Serialization: Arguments passed to C# delegates are stringified using a custom replacer that ignores circular references to prevent errors during the Blazor interop boundary crossing.
  5. Workaround for JSON.NET bug in Client-side Blazor

    master

    Client-side Blazor projects may encounter a bug in JSON.NET related to reflection. Use one of the following workarounds:

    Option 1: Use a Linker.xml (Preferred)

    Add a Linker.xml file to the root of your client-side project to prevent the Mono linker from stripping necessary constructors. Set the build action to BlazorLinkerDescriptor (or add it via .csproj).

    Example .csproj entry:

    <ItemGroup>
        <BlazorLinkerDescriptor Include="Linker.xml" />
    </ItemGroup>

    Example Linker.xml content:

    <?xml version="1.0" encoding="UTF-8" ?>
    <linker>
        <assembly fullname="mscorlib">
            <type fullname="System.Threading.WasmRuntime" />
        </assembly>
        <assembly fullname="System.Core">
            <type fullname="System.Linq.Expressions*" />
        </assembly>
        <assembly fullname="ChartJs.Blazor.Sample.ClientSide" />
        <assembly fullname="System">
            <type fullname="System.ComponentModel.ReferenceConverter">
                <method signature="System.Void .ctor(System.Type)" />
            </type>
        </assembly>
    </linker>

    Option 2: Manual ReferenceConverter invocation

    Manually invoke the ReferenceConverter constructor in your code to ensure it isn't optimized away:

    private ReferenceConverter ReferenceConverter = new ReferenceConverter(typeof(Chart));
  6. Create a Pie Chart in Blazor

    master

    To create a chart, follow these steps:

    1. Add the specific chart namespace (e.g., @using ChartJs.Blazor.PieChart) to your component.
    2. Add the <Chart> component to your markup, passing a configuration object to the Config parameter.
    3. Initialize the configuration object in the @code block, defining Options, Data.Labels, and Data.Datasets.
    @using ChartJs.Blazor
    @using ChartJs.Blazor.PieChart
    
    <Chart Config="_config"></Chart>
    
    @code {
        private PieConfig _config;
    
        protected override void OnInitialized()
        {
            _config = new PieConfig
            {
                Options = new PieOptions
                {
                    Responsive = true,
                    Title = new OptionsTitle
                    {
                        Display = true,
                        Text = "ChartJs.Blazor Pie Chart"
                    }
                }
            };
    
            foreach (string color in new[] { "Red", "Yellow", "Green", "Blue" })
            {
                _config.Data.Labels.Add(color);
            }
    
            PieDataset<int> dataset = new PieDataset<int>(new[] { 6, 5, 3, 7 })
            {
                BackgroundColor = new[]
                {
                    ColorUtil.ColorHexString(255, 99, 132),
                    ColorUtil.ColorHexString(255, 205, 86),
                    ColorUtil.ColorHexString(75, 192, 192),
                    ColorUtil.ColorHexString(54, 162, 235),
                }
            };
    
            _config.Data.Datasets.Add(dataset);
        }
    }
  7. Initialize or update a chart via ChartJsInterop

    master

    The ChartJsInterop class provides the primary interface for managing Chart.js instances from Blazor. It handles both the initial creation of a chart and subsequent updates to existing charts.

    • setupChart(config): Initializes a new chart using the provided ChartConfiguration. If a chart with the same canvasId already exists, it automatically redirects to updateChart instead.
    • updateChart(config): Updates an existing chart. It performs a smart merge of datasets and labels to preserve array references, which helps maintain smooth Chart.js animations. It also extends existing options with new ones provided in the config (note: it does not delete existing options).

    To ensure smooth updates, datasets should include an id property so the interop can match new datasets to existing ones.

    // Conceptual usage of the interop methods
    // setupChart(config: ChartConfiguration): boolean
    // updateChart(config: ChartConfiguration): boolean
  8. Supported Chart.js callback hooks

    master

    The ChartJsInterop automatically wires up several Chart.js callback options to allow Blazor integration. When you provide a configuration, the following hooks are processed:

    • General Options: onClick, onHover
    • Legend Options: legend.onClick, legend.onHover, legend.labels.filter, legend.labels.generateLabels
    • Scales/Ticks: scales.xAxes.ticks.callback, scales.yAxes.ticks.callback, and scale.ticks.callback (for newer Chart.js versions).

    If a handler is not provided for a specific hook, the interop falls back to the default Chart.js behavior.