KristofferStrube / Blazor.Popper

A Blazor wrapper for the Javascript library Popper.js
MIT License
27 stars 5 forks source link

Unhandled Null Exception #7

Open HowardFedTec opened 1 month ago

HowardFedTec commented 1 month ago

Issue: I'm encountering an unhandled null exception using this library.

The exception occurs randomly.

Cannot read properties of null (reading 'assignedSlot') TypeError: Cannot read properties of null (reading 'assignedSlot') at b (https://unpkg.com/@popperjs/core@2:5:2514) at x (https://unpkg.com/@popperjs/core@2:5:2668) at w (https://unpkg.com/@popperjs/core@2:5:2721) at Object.setOptions (https://unpkg.com/@popperjs/core@2:5:8209) at Module.setOptionsOnInstance (https://localhost:4343/_content/KristofferStrube.Blazor.Popper/KristofferStrube.Blazor.popper.js:43:21)

at Microsoft.JSInterop.JSRuntime.InvokeAsync[TValue](Int64 targetInstanceId, String identifier, Object[] args) at KristofferStrube.Blazor.Popper.Instance.SetOptions(Options options) at KristofferStrube.Blazor.Popper.TogglePopperComponent.OpdatePopperAsync() at KristofferStrube.Blazor.Popper.TogglePopperComponent.PointerMove() at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task) at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState) An unhandled error has occurred. Usually an invalid page request.

Configuration: Visual Studio 2022 Version 17.11.4 Blazor Server-Side application .Net 8

dotnet --info .NET SDK: Version: 8.0.401 Commit: 811edcc344 Workload version: 8.0.400-manifests.251308be MSBuild version: 17.11.4+37eb419ad

Runtime Environment: OS Name: Windows OS Version: 10.0.22631 OS Platform: Windows RID: win-x64 Base Path: C:\Program Files\dotnet\sdk\8.0.401\

.NET workloads installed: Configured to use loose manifests when installing new manifests. [android] Installation Source: VS 17.11.35312.102 Manifest Version: 34.0.113/8.0.100 Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.sdk.android\34.0.113\WorkloadManifest.json Install Type: FileBased

[maui-windows] Installation Source: VS 17.11.35312.102 Manifest Version: 8.0.82/8.0.100 Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.sdk.maui\8.0.82\WorkloadManifest.json Install Type: FileBased

[maccatalyst] Installation Source: VS 17.11.35312.102 Manifest Version: 17.5.8030/8.0.100 Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.sdk.maccatalyst\17.5.8030\WorkloadManifest.json Install Type: FileBased

[ios] Installation Source: VS 17.11.35312.102 Manifest Version: 17.5.8030/8.0.100 Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.sdk.ios\17.5.8030\WorkloadManifest.json Install Type: FileBased

[wasm-tools-net6] Installation Source: VS 17.11.35312.102 Manifest Version: 8.0.8/8.0.100 Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.workload.mono.toolchain.net6\8.0.8\WorkloadManifest.json Install Type: FileBased

[wasm-tools-net7] Installation Source: VS 17.11.35312.102 Manifest Version: 8.0.8/8.0.100 Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.workload.mono.toolchain.net7\8.0.8\WorkloadManifest.json Install Type: FileBased

[wasm-tools] Installation Source: VS 17.11.35312.102 Manifest Version: 8.0.8/8.0.100 Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.workload.mono.toolchain.current\8.0.8\WorkloadManifest.json Install Type: FileBased

[aspire] Installation Source: VS 17.11.35312.102 Manifest Version: 8.1.0/8.0.100 Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.sdk.aspire\8.1.0\WorkloadManifest.json Install Type: FileBased

Host: Version: 8.0.8 Architecture: x64 Commit: 08338fcaa5

.NET SDKs installed: 8.0.400 [C:\Program Files\dotnet\sdk] 8.0.401 [C:\Program Files\dotnet\sdk]

.NET runtimes installed: Microsoft.AspNetCore.App 6.0.33 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 8.0.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.33 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 8.0.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 6.0.33 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 8.0.8 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found: x86 [C:\Program Files (x86)\dotnet] registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables: Not set

global.json file: Not found

HowardFedTec commented 1 month ago

Modifying target framework to .NET 8 & upgrading all packages from 6.x.x to 8.x.x reduced the null exceptions.

However, I think there is a missing parent relationship between the arrow and the popper element.

KristofferStrube commented 1 month ago

Hey @HowardFedTec I think you are right in it probably being caused by a mismatch in what versions of the component libraries and .NET version you were using. I have had the idea of making the dependencies differ depending on the target .NET version. This points to that idea as something that should be prioritized by me.

With regards to the other part with the arrow then you might be missing some of the CSS that styles the arrow in my sample app: https://github.com/KristofferStrube/Blazor.Popper/blob/main/samples/KristofferStrube.Blazor.Popper.Samples/wwwroot/css/app.css#L205-L239

HowardFedTec commented 1 month ago

Hi.

Thank you for responding.

I forked your project. There are a couple of issues I found.

  1. The .Net versions caused some issues. The Microsoft Components package seems to be the primary culprit. After upgrading the projects to .Net 8 and all the packages to the latest version. That helped, but I still received different errors.
  2. If the user is in the start of or middle of navigation and they mouseover a popper, it throws an exception because the DOM element is already destroyed. I fixed that with a couple of try catches.
  3. I did pull in your css, but the arrow still throws an exception also. I've set AddArrow="false" at the moment and am not using arrows and I have no issue. I have no fix for the arrow exception. I will double check and make sure I captured all the required css.

I can send you my code changes for #2 if you'd like me to. It's a simple fix.

Howard

KristofferStrube commented 1 month ago

Is point 2 specifically for the TogglePopperComponent?

HowardFedTec commented 1 month ago

Change 1:

Problem: The popper reference is null. Possibly becuase dom hasn't created it yet, so no options can be set.

Fix: Catch and ignore the exception. It will be recreated on the next mouse action, or so it appears.

File: popper.js

export async function setOptionsOnInstance(instance, options) {
    await cleanOptions(options);

    try {
        return instance.setOptions(options).then(state => stripState(state));
    }
    catch (e) {
        //console.error(e);
    }
}

Change 2:

Problem: Upon navigating away from the page, the object reference(s) may not exist depending on the state of the DOM.

Fix: Catch and ignore the exception. The instance will be recreated on the next action and exists. Created instance variables at the top of the method. Return instance when successful. Return null when exception encountered.

File: Popper.cs


using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System;
using System.Threading.Tasks;

namespace USMC.TECOM.Blazor.Popper;

public class Popper
{
    private readonly IJSRuntime jSRuntime;

    public Popper(IJSRuntime jSRuntime)
    {
        this.jSRuntime = jSRuntime;
    }

    public async Task<Instance> CreatePopperAsync(ElementReference reference, ElementReference popper, Options options)
    {
        IJSObjectReference jSInstance=null;
        if (jSRuntime is IJSInProcessRuntime)
        {
            try
            {
                IJSInProcessObjectReference popperWrapper = await jSRuntime.InvokeAsync<IJSInProcessObjectReference>("import", "./_content/USMC.TECOM.Blazor.Popper/USMC.TECOM.Blazor.popper.js");
                jSInstance = await popperWrapper.InvokeAsync<IJSObjectReference>("createPopper", reference, popper, options);
#pragma warning disable CS0618 // Type or member is obsolete
                return new(jSInstance, popperWrapper);
#pragma warning restore CS0618 // Type or member is obsolete
            }
            catch (Exception ex)
            {
                //Console.WriteLine(ex.Message);
            }
        }
        else
        {
            try
            {
                IJSObjectReference popperWrapper = await jSRuntime.InvokeAsync<IJSObjectReference>("import", "./_content/USMC.TECOM.Blazor.Popper/USMC.TECOM.Blazor.popper.js");
                jSInstance = await popperWrapper.InvokeAsync<IJSObjectReference>("createPopperAsync", reference, popper, options);
                return new(jSInstance, popperWrapper);
            }
            catch (Exception ex)
            {
               // This is the main problem exception when the instance cannot be created. 
               // Console.WriteLine(ex.Message);
            }
        }
        return null;
    }

    public async Task<Instance> CreatePopperAsync(VirtualElement reference, ElementReference popper, Options options)
    {
        IJSObjectReference jSInstance = null;
        if (jSRuntime is IJSInProcessRuntime)
        {
            try
            {
                IJSInProcessObjectReference popperWrapper = await jSRuntime.InvokeAsync<IJSInProcessObjectReference>("import", "./_content/USMC.TECOM.Blazor.Popper/USMC.TECOM.Blazor.popper.js");
                jSInstance = await popperWrapper.InvokeAsync<IJSObjectReference>("createPopper", reference, popper, options);
#pragma warning disable CS0618 // Type or member is obsolete
                return new(jSInstance, popperWrapper);
#pragma warning restore CS0618 // Type or member is obsolete
            }
            catch (Exception ex)
            {
                //Console.WriteLine(ex.Message);
            }
        }
        else
        {
            try
            {
                IJSObjectReference popperWrapper = await jSRuntime.InvokeAsync<IJSObjectReference>("import", "./_content/USMC.TECOM.Blazor.Popper/USMC.TECOM.Blazor.popper.js");
                jSInstance = await popperWrapper.InvokeAsync<IJSObjectReference>("createPopperAsync", reference, popper, options);
                return new(jSInstance, popperWrapper);
            }
            catch (Exception ex)
            {
                //Console.WriteLine(ex.Message);
            }
        }
        return null;
    }
}
KristofferStrube commented 1 month ago

I haven't seen it fail like that before. If you could supply a minimal sample that I could clone to get the error myself then I would love to see if there is something more fundamental I could fix instead of treating the symptom.

HowardFedTec commented 1 month ago

I'd be happy to. No guarantee on my timing though. I'm preparing for a customer demonstration for one of my projects this week.

KristofferStrube commented 1 month ago

No rush. I'm also pretty tied up this week preparing for a talk. 😊