How to bind a reference to a component in blazor using the RenderTreeBuilder?
Asked Answered
F

1

6

I would like to know how I can implement the @ref attribute using the RenderTreeBuilder. Following blazor code is a minimalistic example to discuss about a solution:

<MudForm @ref="_Form">@ChildContent</MudForm>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    private MudForm _Form;
}

I would like to reimplement the blazor code referring to the MudForm using the RenderTreeBuilder. Therefore I already tried the following approach but it didn't work for me:

private RenderFragment formControl => (builder) =>
    {
        builder.OpenComponent<MudForm>(0);
        builder.AddElementReferenceCapture(1, (value) => { _Form = value.Context; });
        builder.AddAttribute(2, nameof(MudForm.ChildContent), ChildContent);
        builder.CloseComponent();
    };

I don't know if the AddElementReferenceCapture-method is correct to bind _Form to the components instance using the RenderTreeBuilder. Could someone please help me with this?

Frisette answered 19/7, 2022 at 10:28 Comment(0)
H
7

I think you need to cast value as it's provided as an object.

See the code below:

builder.AddElementReferenceCapture(1, (value) => { _Form = (MudForm)value; });

For reference:

You can get the Razor Compiler to emit the class files by adding this line to the project file:

  <PropertyGroup>
  .....
      <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
  </PropertyGroup>

And then view the files in /obj/Debug/net6.0/generated/....

Here's a simple Razor file:

@page "/"

<h1>Hello, world!</h1>

<MinimumComponent @ref=this.myComponent/>

@code {
    private MinimumComponent myComponent;
}

And the Razor compiler generated code:

    public partial class Test : Microsoft.AspNetCore.Components.ComponentBase
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.AddMarkupContent(0, "<h1>Hello, world!</h1>\r\n\r\n");
            __builder.OpenComponent<BlazorApp3.Data.MinimumComponent>(1);
            __builder.AddComponentReferenceCapture(2, (__value) => {
#nullable restore
#line 5 "C:\Users\shaun\source\repos\BlazorApp3\BlazorApp3\Pages\Test.razor"
                       this.myComponent = (BlazorApp3.Data.MinimumComponent)__value;

#line default
#line hidden
#nullable disable
            }
            );
            __builder.CloseComponent();
        }
        #pragma warning restore 1998
#nullable restore
#line 7 "C:\Users\shaun\source\repos\BlazorApp3\BlazorApp3\Pages\Test.razor"
       
    private MinimumComponent myComponent;

#line default
#line hidden
#nullable disable
    }

Here's a screenshot of my setup showing the location of the compiled Razor files.

enter image description here

And my project file:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
      <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
  </PropertyGroup>

</Project>
Halona answered 19/7, 2022 at 12:33 Comment(4)
Thank you very much actually this worked for me I added following line to the RenderTreeBuilder builder.AddComponentReferenceCapture(6, (value) => { _Form = value as MudForm; }); and this worked for meClaret
@MarcelMüller - Two ways of doing the same thing. As is quicker, but as the MS coders used the cast here, I'd stick to the cast. As will return a null if there's a conversion failure (you tried to convert to the wrong component type). The cast will throw an exception which is what you almost certainly want as it will make debugging much easier!Halona
Could you please let me know where to find the generated blazor file from your answer? I can't find it under /obj/Debug/net6.0/generated/....Claret
See my answer - I've added a screenshot of my testing solution.Halona

© 2022 - 2024 — McMap. All rights reserved.