Search code examples
blazornullreferenceexceptionblazor-webassemblyblazor-jsinterop

Blazor: ElementReference not present ChildComponent


i need to pass a ElementReference to a child component of a Blazor component for interop. But when i do so the interop for the child component only works after the interop for the parent component executed successfully. I guess that the Reference is not presend, but this does not make any sense for me since i alwas use the ElementReference in code that is executed after render.

Parent:

@page "/"
@inject IJSRuntime JsRuntime

<h1>Hello, world!</h1>

Welcome to your new app.
<p @ref="ExampleRef">Test:</p>

<ChildComponent Reference="ExampleRef"/>
<button @onclick="HandleClick">Click 2</button>

@code
{
    public ElementReference ExampleRef;

    public void HandleClick(MouseEventArgs mouseEventArgs) => ((JSInProcessRuntime)JsRuntime).InvokeVoid("foo", ExampleRef, "Parent Clicked! ");
}

Child:

<button @onclick="HandleClick">Click</button>

@inject IJSRuntime JsRuntime
@code {
    [Parameter]
    public ElementReference Reference { get; set; }

    protected override void OnAfterRender(bool firstRender)
    {
        base.OnAfterRender(firstRender);
        if(firstRender)
            SingeltonFoo.Instance.SomethingHappened += HandleSomethingHappened;

    }
    public void HandleSomethingHappened(string foo) => ((JSInProcessRuntime)JsRuntime).InvokeVoid("foo", Reference, foo);
    public void HandleClick(MouseEventArgs mouseEventArgs) => SingeltonFoo.Instance.OnSomethingHappend("Child Clicked! ");
}

Intop Code:

window.foo = (ele, foo) => {
    console.log(ele);
    ele.innerHTML = ele.innerHTML + foo;
}

Full Source code at: https://github.com/niklasweimann/BlazorDebugging/tree/elementReferene-null/BlazorDebugging (Note: Its a repo with multiple branches for showing of problems.)

Background: I need to get the position of an HTML element relative to the browserwindow and i need to get the center point of a div element.


Solution

  • ElementReference instances are only valid after the component in which they are defined has been rendered. If you pass them down to child component I believe there's no guarantee they'll work correctly, as in Blazor components render independently (A parent re-rendering doesn't mean a child re-renders) so you can't know from the child component whether the reference is correct or not.

    To achieve your scenario, it would make more sense to simply perform a JS Interop call inside the OnAfterRenderAsync method of the parent component