Home > other >  DynamicComponent send value from child to the parent
DynamicComponent send value from child to the parent

Time:07-07

I have a class that represents the necessary data for creating a DynamicComponent:

public class ComponentMetadata
{
    public string? Name { get; set; }
    public Dictionary<string, object> Parameters { get; set; } = 
        new Dictionary<string, object>();
}

I have a custom table component that uses ComponentMetadata to add some dynamic component to the table:

TableTemplate.razor:

<table >
    <thead>
        <tr>@TableHeader</tr>
    </thead>
    <tbody>
        @foreach (var RowTemplateConfig in RowTemplateConfigs)
        {
            if (RowTemplateConfig.DataItem is not null)
            {
                <tr>
                  <td> @RowTemplateConfig.DataItem </td>
                 
                 @if(RowTemplateConfig.ComponentMetadata is not null)
                 {
                  <td>
                    <DynamicComponent
                     Type= RowTemplateConfig.ComponentMetadata.Name
                     Parameters= RowTemplateConfig.ComponentMetadata.Parameters />
                  </td>
                 }
               </tr>
           }
       }
    </tbody>
</table>

@code {
    public class RowTemplateConfig 
    {
      public ComponentMetadata ComponentMetadata { get; set;}
      public string DataItem { get; set; }
    }
    [Parameter]
    public RenderFragment? TableHeader { get; set; }

    [Parameter]
    public List<RowTemplateConfig> RowTemplateConfigs {get; set;}
}

I have also other custom components for example Button custom component:

MyButton.razor

<button type="button"
        
        @onclick="BtnClick">
@Text
</button>

@code {
 [Parameter]
 public string Style { get; set; }
 [Parameter]
 public string Text { get; set; }
 [Parameter]
 public EventCallback<object> OnBtnClick { get; set; }

 private async Task BtnClick(object item) 
 {
  await OnBtnClick.InvokeAsync(item);
 }
}

I want to use my TableTemplate.razor in some razor page:

pets1.razor

@page "/pets1"

<h1>Pets</h1>

<TableTemplate RowTemplateConfigs =RowTemplateConfigs  TItem =string>
    <TableHeader>
        <th>Name</th>
    </TableHeader>
</TableTemplate>

@code {
    private List<RowTemplateConfig> RowTemplateConfigs = new()
    {
        RowTemplateConfig = new () 
        {
          DataItem = "Bigglesworth",
          ComponentMetadata = new()
          {
           Name= typeof(MyButton).
           Parameters = new Dictionary<string, object>()
           {
            { nameof(MyButton.Style), "btn btn-outline-success" }
            { nameof(MyButton.OnBtnClick),  EventCallback.Factory.Create<object>(this, OnTestClick)},
            { nameof(MyButton.Text), "Hi"}
           }
          }
        },
        RowTemplateConfig = new () 
        {
          DataItem = "Saberhagen",
          ComponentMetadata = new()
          {
           Name= typeof(MyButton).
           Parameters = new Dictionary<string, object>()
           {
            { nameof(MyButton.Style), "btn btn-outline-success" }
            { nameof(MyButton.OnBtnClick),  EventCallback.Factory.Create<object>(this, OnTestClick)},
            { nameof(MyButton.Text), "Hi"}
           }
          }
        },
       //rest of the lest ...
    }

 private async Task OnTestClick(object sender)
 {
  // I want to catch the name of the pet here when user click on the button
   await Task.Delay(20);
 }
}

My Question:

When user clicks on MyButton in TableTemplate in pets1.razor I want to catch the element (name of pet) in the event OnTestClick.

Thanks and sorry for long question, I appreciate any suggestion.

CodePudding user response:

You need to add a Pet Name parameter to the button component, load it and then submit the property back on the callback event.

Here's a modified version of your code (with a few typo and type fixes). Note: I code with Nullable enabled.

ComponentMetadata

public class ComponentMetadata
{
    public Type Name { get; set; } = default!;
    public Dictionary<string, object> Parameters { get; set; } =
        new Dictionary<string, object>();
}

MyButton.razor

<button type="button"  @onclick=BtnClick>
@Text
</button>

@code {
 [Parameter] public string? Style { get; set; }
 [Parameter] public string? Name { get; set; }
 [Parameter] public string? Text { get; set; }
 [Parameter] public EventCallback<string> OnBtnClick { get; set; }

 private async Task BtnClick() 
    =>  await OnBtnClick.InvokeAsync(this.Name);
}

And then the rest of the code rolled into my test page:

@page "/"

@foreach (var component in myDynamicComponents)
{
    <DynamicComponent Type="@component.ComponentMetadata.Name" Parameters=component.ComponentMetadata.Parameters />
}

<div >
    <strong>Message:</strong> @message
</div>
@code {
    private string message = string.Empty;

    private RowTemplateConfig GetRowTemplate(string petName)
        => new RowTemplateConfig
            {
                DataItem = petName,
                ComponentMetadata =
                new ComponentMetadata()
                {
                    Name = typeof(MyButton),
                    Parameters = new Dictionary<string, object>()
                       {
                    { nameof(MyButton.Style), "btn btn-outline-success ms-1" },
                    { nameof(MyButton.Name), petName },
                    { nameof(MyButton.OnBtnClick),  EventCallback.Factory.Create<string>(this, OnTestClick)},
                    { nameof(MyButton.Text), $"Hi {petName}"}
                       }
                }
            };

    private List<RowTemplateConfig> myDynamicComponents => new List<RowTemplateConfig> {
            GetRowTemplate("Ginger"),
            GetRowTemplate("Tiger"),
            GetRowTemplate("Felix")
        };

    private async Task OnTestClick(string name)
    {
        await Task.Delay(20);
        message = $"{name} Clicked at {DateTime.Now.ToLongTimeString()}";
    }

    public class RowTemplateConfig
    {
        public ComponentMetadata ComponentMetadata { get; set; } = default!;
        public string? DataItem { get; set; }
    }

}
  • Related