plotlyblazormonaco-editorplotly.net

Can't use Plotly.Blazor if BlazorMonaco is installed in my Blazor App


In my Blazor app I'm using Plotly.Blazor for showing some graphs.
I have recently added BlazorMonaco, but once added - Plotly.Blazor stopped working.

In the DevTools console I get the following error:

Error: Microsoft.JSInterop.JSException: Cannot read properties of
undefined (reading 'newPlot') TypeError: Cannot read properties of
undefined (reading 'newPlot')
at Module.newPlot (https://localhost:5001/_content/Plotly.Blazor/plotly-interop.js:4:19)
at https://localhost:5001/_framework/blazor.server.js:1:3244
...

After exploring a bit:

  1. If I don't reference Monaco's _content/BlazorMonaco/lib/monaco-editor/min/vs/loader.js file, Plotly works just fine (but than of course Monaco wont work).
  2. When debugging Plotly's js it seems that window.Plotly is not defined.

My conclusion is that Monaco is probably polluting the global namespace, but I cannot find hard evidence for that...
Any suggestions how I can work around this issue?

Additional information:


Solution

  • The issue is that the importScript function in plotly-interop-5.4.0.js conflicts with AMD (Asynchronous Module Definition) loaders, such as RequireJS, which might be active in your Blazor environment (such as used in Blazor.Monaco).

    Plotly tries to define itself as an anonymous module, but AMD doesn't support multiple anonymous define calls in the same script so when onload the window.Plotly is undefined as you've seen.

    Update: Fixed released 5.4.1

    To resolve this issue, I submitted a pull request, but in the meantime, you can do this workaround.

    Create two js scripts in separate folder if using RCL or in the index.html

    function disableAMD() {
        if (typeof Plotly !== "undefined") {
            return;
        }
        
        if (typeof window.define === 'function' && window.define.amd) {
            window._originalDefine = define;
            window.define = undefined; // Temporarily disable AMD
        }
    }
    
    function enableAMD() {
        if (window._originalDefine) {
            window.define = window._originalDefine; // Restore the original define function
            delete window._originalDefine;
        }
    }
    

    Then call them in the OnInitializedAsync() when your form that uses Plotly.Blazor

    protected override async Task OnInitializedAsync()
        {
            var module = await Module;
            await module.InvokeVoidAsync("disableAMD");
            await base.OnInitializedAsync();
        }
    

    Then restore amd when you close the form:

    public async ValueTask DisposeAsync()
        {
            _chart?.Purge();
            var module = await Module;
            await module.InvokeVoidAsync("enableAMD");
            await module.DisposeAsync();
        }