-
Notifications
You must be signed in to change notification settings - Fork 4
Callbacks and Plugins
Chart.js callbacks and plugins are JavaScript features. pax.BlazorChartJs lets C# config objects point to those features without putting raw JavaScript into serialized chart configuration.
The callback sample uses JavaScript to format y-axis ticks, filter legend labels, and draw a patterned dataset fill.

Register a callback module when the app configures Chart.js:
builder.Services.AddChartJs(options =>
{
options.ChartJsCallbacksModuleLocation =
$"{builder.HostEnvironment.BaseAddress}_content/pax.BlazorChartJs.samplelib/chartJsCallbacks.js";
});That module exports named callbacks through chartJsCallbacks. The C# chart config references those names with ChartJsFunction.FromName(...).
new BarDataset
{
Label = "Dataset 1",
Data = [1, 2, 3],
BackgroundColor = ChartJsFunction.FromName("createRepeatFillPattern")
}
new Legend
{
Labels = new Labels
{
Filter = ChartJsFunction.FromName("showLegendItem")
}
}
new LinearAxisTick
{
Callback = ChartJsFunction.FromName("formatCurrency")
}The callback names above map to JavaScript functions in the module. A small chartJsCallbacks.js for this chart can look like this:
const fillPatternCache = new WeakMap();
function createFillPattern(chart) {
let pattern = fillPatternCache.get(chart);
if (pattern) {
return pattern;
}
const patternCanvas = document.createElement('canvas');
patternCanvas.width = 8;
patternCanvas.height = 8;
const patternContext = patternCanvas.getContext('2d');
patternContext.fillStyle = '#2563eb';
patternContext.fillRect(0, 0, 8, 8);
patternContext.fillStyle = '#facc15';
patternContext.fillRect(0, 0, 4, 4);
patternContext.fillRect(4, 4, 4, 4);
pattern = chart.ctx.createPattern(patternCanvas, 'repeat');
fillPatternCache.set(chart, pattern);
return pattern;
}
export const chartJsCallbacks = Object.freeze({
formatCurrency(value) {
return `$${value}`;
},
showLegendItem() {
return true;
},
createRepeatFillPattern(context) {
return createFillPattern(context.chart);
}
});The public sample callback file exports these names with a larger callback registry used by the rest of the sample app.
Use this pattern when Chart.js expects a function for:
- tick or tooltip formatting
- legend label filtering and sorting
- scriptable colors, point styles, sizes, or titles
- dataset options that depend on the drawing context
The custom plugin sample registers chartjs-plugin-annotation from JavaScript, then exposes the annotation plugin options through derived C# option types.
private async Task RegisterPlugin()
{
if (moduleTask is not null)
{
var module = await moduleTask.Value.ConfigureAwait(false);
await module.InvokeVoidAsync("registerPlugin").ConfigureAwait(false);
}
}The JavaScript module owns the actual Chart.js registration:
import * as annotationPlugin from './chartjs-plugin-annotation.min.js';
export async function registerPlugin() {
await import('./chartjs-plugin-annotation.min.js');
Chart.register(annotationPlugin);
}The sample uses derived config types so Plugins.Annotation can be configured from C#:
public class CustomChartJsConfig : ChartJsConfig
{
public new CustomChartJsOptions? Options { get; set; }
}
public record CustomChartJsOptions : ChartJsOptions
{
public new CustomPlugins? Plugins { get; set; }
}
public record CustomPlugins : Plugins
{
public AnnotationsSettings? Annotation { get; set; }
}After the plugin is registered and plugin options are assigned, the sample calls chartConfig.ReinitializeChart() so Chart.js constructs the chart with the plugin behavior available.
The bar chart sample uses the same custom-plugin idea to draw images over individual bars. Its ShowImages action registers an image plugin and fills a custom BarIcons plugin option with one icon entry per bar.
![]()
The plugin option needs a C# shape that can serialize Plugins.BarIcons into Chart.js plugin options:
public class IconsChartJsConfig : ChartJsConfig
{
public new IconsChartJsOptions? Options { get; set; }
}
public record IconsChartJsOptions : ChartJsOptions
{
public new IconsPlugins? Plugins { get; set; }
}
public record IconsPlugins : Plugins
{
public ICollection<ChartIconsConfig>? BarIcons { get; set; }
}
public record ChartIconsConfig
{
public int XWidth { get; set; }
public int YWidth { get; set; }
public int YOffset { get; set; }
public string ImageSrc { get; set; } = null!;
}The Blazor component imports the JavaScript module, registers the plugin, assigns the icon options, and rebuilds the chart:
var module = await moduleTask.Value.ConfigureAwait(false);
await module.InvokeVoidAsync("registerImagePlugin").ConfigureAwait(false);
chartJsConfig.Options!.Plugins!.BarIcons =
[
new ChartIconsConfig
{
XWidth = 30,
YWidth = 30,
ImageSrc = "_content/my-app/images/alpha.png"
},
new ChartIconsConfig
{
XWidth = 30,
YWidth = 30,
ImageSrc = "_content/my-app/images/beta.png"
}
];
chartJsConfig.ReinitializeChart();The Chart.js plugin id must match the serialized option name: barIcons. In afterDatasetDraw, the plugin gets the rendered bar elements from dataset metadata and draws each icon at the matching bar index.
const iconCache = new Map();
function getIcon(chart, src) {
let image = iconCache.get(src);
if (image) {
return image;
}
image = new Image();
image.onload = () => chart.draw();
image.src = src;
iconCache.set(src, image);
return image;
}
export function registerImagePlugin() {
Chart.register({
id: 'barIcons',
afterDatasetDraw(chart, _args, icons) {
const { ctx, scales } = chart;
const bars = chart.getDatasetMeta(0).data;
icons.forEach((icon, index) => {
const bar = bars[index];
const image = getIcon(chart, icon.imageSrc);
if (!bar || !image.complete || image.naturalWidth === 0) {
return;
}
const value = Number(bar.$context.raw);
const x = bar.x - (icon.xWidth / 2);
const barEndY = value < 0
? scales.y.getPixelForValue(0)
: bar.y;
const y = barEndY - icon.yWidth + icon.yOffset;
ctx.drawImage(image, x, y, icon.xWidth, icon.yWidth);
});
}
});
}The sample app preloads its small icon set before drawing. For charts with negative values, placing negative-bar icons relative to the y-axis zero pixel keeps them above the zero line, like the rating-gain chart in dsstats.
- EventcallbackChartComp.razor
- chartJsCallbacks.js
- CustomPluginComp.razor
- BarChartComp.razor.cs
- customPlugin.js
- Callback live sample
- Bar chart live sample - click ShowImages to apply the icon plugin.