Custom JavaScript function in Blazor

Microsoft Blazor wallpaper

In this new post custom JavaScript function in Blazor, I present how to create functions in C# in a Blazor page and integrate them with JavaScript. What I explain in this post, it is coming fro my ChartJs component for Blazor: this component allows you to create nice graphs using ChartJs library.

Scenario

The ChartJs library allows the developers to customise the chart using callbacks and here developers can write their own JavaScript code. For example, if you want to customise the Tooltip for each point, you can use the Tooltip Callbacks

const chart = new Chart(ctx, {
    type: 'line',
    data: data,
    options: {
        plugins: {
            tooltip: {
                callbacks: {
                    label: function(context) {
                        let label = context.dataset.label || '';

                        if (label) {
                            label += ': ';
                        }
                        if (context.parsed.y !== null) {
                            label += new Intl.NumberFormat('en-US', { 
                                           style: 'currency', currency: 'USD' }).format(context.parsed.y);
                        }
                        return label;
                    }
                }
            }
        }
    }
});

So, if you look at the lines 6-18, the callbacks has a property label and here we can write our JavaScript code. Then, when the graph renders the tooltip for a point, the library calls the callbacks function and displays the label in the format you define. Here the screenshot for the code above.

ChartJs custom tooltip - Custom JavaScript function in Blazor

Here, you can try yourself with this Codepen. When you move the mouse over a point, the tooltip is displaying the custom label.

So, the problem is how to replicate the same functionalities in the Blazor component and allow developers to create their custom code.

Then, I was looking for an implementation where I can add in a Blazor page the JavaScript code and call it in some way (see my first post in the Microsoft Learn). Nothing was working.

I couldn’t understand how to pass data and communicate among the JavaScript code, the Blazor component and the Blazor Page. Most of the people redirected me to the Microsoft Learn page called “Call JavaScript functions from .NET methods in ASP.NET Core Blazor” but wasn’t useful. After few months, a pull request on GitHub from Macias opened my mind.

Anatomy of the component

First, just few words about the ChartJs component for Blazor and how it is working. Obviously this is an example to implement your custom JavaScript function in your Blazor components and projects.

Anatomy of the component - Custom JavaScript function in Blazor
Anatomy of the ChartJS component for Blazor

So, I’m telling you all this stuff to explain my thoughts about the component. But also, how I discovered the correct implementation.

Chart.js script

First, the Chart.js script is the core of all component. As I said, this is based on the javascript library ChartJs. So, the component is a faced of it adding the ability to use it in Blazor applications. So, this script calls and communicates with the ChartJs library to create the chart.

The component exposes the properties, methods and events to developers in their Blazor pages. The real definition of the settings are coming from the Blazor page. Then, the script initialises the chart calling the ChartJS library.

In this script, there are other functions available out-of-the-box, such as the crosshair. Also, the component raises events and calls to the Blazor component. For example, when the user clicks on the chart, the Chart.js invoke ChartClick in the Blazor component.

Blazor component

In order to create the chart, the script has to receives the configuration of the chart from the Blazor component. The component is simplified the creation exposing properties, events and methods that developers can use in their applications.

So, in same way I have to add here something that developers can use to add their code in C#.

Blazor page

Finally, this is the page where we want to generate the chart, passing to the component all the settings. Also, in here I want to add my custom code to change the chart.

What I need to do

So, focus on the Tooltip callbacks, what I need is:

  • the JavaScript has to call the Blazor component for a particular event (in this case when there is a callback during the creation of a tooltip)
  • the Blazor component must to receive the call or connect the code between the JavaScript code and the custom code in the page
  • the Blazor component must call the custom code, pass parameters if it needs them and sends back the result

Implementation in the component

Now, we know the structure of the component and what I want to do. So, we can start to analyse how to implement the solution. First, in the component I define

private DotNetObjectReference<IChartConfig>? dotNetObjectRef;

As the name says, this is a reference of my chart configuration that has all the data, labels and options. When the Chart.js calls a function in this component, it will pass always this object to access the data and options.

Then, I have to define a function that Chart.js can call. For that, I have to define a JSInvokable function like that

[JSInvokable]
public static string[] TooltipCallbacksLabel(
    DotNetObjectReference<IChartConfig> config, int[] parameters)
{
     var ctx = new CallbackGenericContext(parameters[0], parameters[1]);
     if (config.Value.Options is Options options)
         return options.Plugins.Tooltip.Callbacks.Label(ctx);
    else
        throw new NotSupportedException();
}

So, what is it happing here? The magic Is here. The Chart.js calls TooltipCallbacksLabel with 2 parameters: the configuration of the chart and some parameters that in this particular case are the value of a specific point in the chart.

In the config, the component checks if the Options are defined and if there is a particular implementation for the Callbacks.Label. If there is one, it pass the values to the function. Basically, this is how the Blazor component calls a custom C# code in the Blazor pageant return to the Chart.js the result. In this case, the result of the C# is a string.

In the Blazor page

So, let me show you the implementation in the Blazor page.

protected override async Task OnInitializedAsync()
{
    _config1 = new BarChartConfig()
        {
            Options = new Options()
            {
                Responsive = true,
                MaintainAspectRatio = false,
                Plugins = new Plugins()
                {
                    Legend = new Legend()
                    {
                        Align = Align.Center,
                        Display = true,
                        Position = LegendPosition.Right
                    },
                    Tooltip = new Tooltip()
                    {
                        Callbacks = new Callbacks()
                        {
                            Label = (ctx) =>
                            {
                                return new[] { 
                                    $"DataIndex: {ctx.DataIndex}\nDatasetIndex: {ctx.DatasetIndex}" };
                            },
                            Title = (ctx) =>
                            {
                                return new[] { $"This is the value {ctx.Value}" };
                            }
                        }
                    }
                },
                Scales = new Dictionary<string, Axis>()
                {
                    {
                        Scales.XAxisId, new Axis()
                        {
                            Stacked = true,
                            Ticks = new Ticks()
                            {
                                MaxRotation = 0,
                                MinRotation = 0
                            }
                        }
                    },
                    {
                        Scales.YAxisId, new Axis()
                        {
                            Stacked = true
                        }
                    }
                }
            }
        };

Look at the lines between 17 and 30. Here is where the Blazor page receives from the Blazor component the values from the Chart.js script. This code creates a string[] to change the text in the tooltip for a particular point. So, there is a constant communication between the Blazor component and the JavaScript underneath. When it is the code, the Blazor component pass the control to the Blazor page and then the result to the JavaScript.

In the JavaScript

And what is the code in the Chart.js? In the configuration of the chart, I pass the DotNetObjectReference. When the ChartJs library wants to call the label callbacks, there is a function that invoke the JSInvokable function I defined in the Blazor component. The call is using the namespace of the component (in this case PSC.Blazor.Components.Chartjs) and the function I want to call (in this case TooltipCallbacksLabel).

config.options.plugins.tooltip.callbacks.label = function (ctx) {
    return DotNet.invokeMethod('PSC.Blazor.Components.Chartjs', 'TooltipCallbacksLabel',
        dotnetConfig, [ctx.datasetIndex, ctx.dataIndex]);
};

As parameters, the function pass to the Blazor component the chart configuration and an array with some values (in this case datasetIndex and dataIndex available in this function). So, the Blazor component pass the parameters to the custom code in the Blazor page, receives the new string and pass the string to the Chart.js that pass the value to the ChartJs library.

Wrap up

I took almost a year to understand how to create custom JavaScript function in a Blazor component that can communicate among them. I couldn’t find any documentation that explain how to do it. So, I hope this can help someone else.

For more info, you can visit:

Happy coding!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.