Skip to content

Circular JSON convert exception when clicking barchart bar #155

@larschristensen20

Description

@larschristensen20

Hello, I've recently dabbled a bit on how to use certain plugins in combination with this project (specifically the datalabels plugin).

Disclaimer: there is a good chance you have no clue why I am getting these errors, but asking here is worth a try!

My Blazor Service-side project features a fully-fledged barchart with multiple 'levels' where deeper (and more detailed) levels can be accessed by pressing on bars in the chart.
For example
Level 1 -> Year view
Level 2 -> Month view
Level 3 -> Day view
Level 4 -> Hour view.

In my config I add a OnClickHandler


_config = new BarConfig(ChartType.Bar)
                {...},
                        OnClick = new DotNetInstanceClickHandler(OnClickHandler)
                    }
                };

Which is defined as such:


[JSInvokable]
    public void OnClickHandler(object sender, object args)
    {
        
            var clickedLabel = GetClickedLabel(args.ToString());
            if (clickedLabel != "")
            {

                switch (state.CurrentState)
                {
                    case State.Year:
                        state.SelectedYear = clickedLabel;
                        state.CurrentState = State.Month;
                        UpdateGraph(readings.Table1);
                        btnYearClass = "btn-show";
                        this.StateHasChanged();
                        break;
                    case State.Month:
                        state.SelectedMonth = state.GetIndexOfMonth(clickedLabel);
                        state.CurrentState = State.Day;
                        UpdateGraph(readings.Table2);
                        btnMonthClass = "btn-show";
                        this.StateHasChanged();
                        break;
                    case State.Day:
                        if (supplier.ConsumptionDetailLevel > 3)
                        {
                            state.SelectedDay = clickedLabel;
                            state.CurrentState = State.Hour;
                            UpdateGraph(readings.Table3);
                            btnDayClass = "btn-show";
                            this.StateHasChanged();
                            break;
                        }
                        break;
                }
            }
        }
    }

This works perfectly!

The Exception

I have then added a reference to the datalabels plugin for chartjs in my _Host.cshtml
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@0.7.0/dist/chartjs-plugin-datalabels.min.js"></script>
Which results in this chart as I load my chart page:
image

Looks fine?

However, whenever I try to click a bar to move to a deeper level, I get this exception:

blazor.server.js:8 Uncaught (in promise) TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'ni'
    |     property 'config' -> object with constructor 'Object'
    |     property 'data' -> object with constructor 'Object'
    |     ...
    |     index 0 -> object with constructor 'i'
    --- property '_chart' closes the circle
    at JSON.stringify (<anonymous>)
    at c (blazor.server.js:8)
    at e.invokeMethodAsync (blazor.server.js:8)
    at ni.<anonymous> (ChartJsInterop.ts:269)
    at ni.handleEvent (Chart.min.js:7)
    at ni.eventHandler (Chart.min.js:7)
    at i (Chart.min.js:7)
    at HTMLCanvasElement.Fe.<computed> (Chart.min.js:7)

Looking at line 269 in ChartJsInterop:

           // .Net instance method
            else if (typeof iClickHandler === "object" &&
                iClickHandler.hasOwnProperty('instanceRef') &&
                iClickHandler.hasOwnProperty('methodName')) {
                return (() => {
                    const onClickInstanceHandler: { instanceRef: DotNetObjectReference, methodName: string } = <any>iClickHandler;
                    const instanceRef = onClickInstanceHandler.instanceRef;
                    const methodName = onClickInstanceHandler.methodName;

                    return async (sender, args) => {

                        // This is sometimes necessary in order to avoid circular reference errors during JSON serialization
                        args = this.GetCleanArgs(args);

                        await instanceRef.invokeMethodAsync(methodName, sender, args);
                    };
                })();
            }
        } else { // fallback to the default
            return chartJsDefaultHandler;
        }
    };

    private GetCleanArgs = (args) => {
        // ToDo: refactor the function to clean up the args of each chart type 
        return typeof args['map'] === 'function' ?
            args.map(e => {
                    const newE = Object.assign({}, e, {_chart: undefined}, {_xScale: undefined}, {_yScale: undefined});
                    return newE;
                }
            ) : args;
    };

There is the GetCleanArgs method to avoid circular reference errors, so it seems something similar has been dealt with before. Any clue whats missing, as I imagine that the datalabels plugin messes with this in some way?

Let me know if you need any additional resources from me.

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions