如何在运行时将方法名称传递给 Blazor 中 DynamicComponent 生成的组件

How to pass method name at runtime to component generated by DynamicComponent in Blazor

我正在尝试使用 .Net 6 中的 DynamicComponent 在 Blazor Wasm 中呈现动态内容。要呈现的组件在具有以下结构的 JSON 文件中定义。

{
   "type":"MyButton",
   "parameters": {
       "Label":"My Buttom",
       "OnClicked": "DoAction"
   } 
}

DynamicComponent 允许我们使用 Dictionary 将参数传递给呈现的组件,如下面的代码我们可以将 component.parameters 定义为 Dictionary

<DynamicComponent Type=@component.type Parameters=@component.parameters />

在我的 razor 文件中,我定义了“DoAction()”方法。我希望在单击 MyButton 组件时调用此方法。但是我们如何将这个 DoAction() 方法作为 EventCallBack 传递给渲染的组件呢?

MyButton.razor 分量:

<button class="btn btn-primary" @onclick="HandledClicked">@LabelText</button>

@code{
  [Parameter]
  public string LabelText {get; set;}

  [Parameter]
  public EventCallback OnClicked { get; set; }

  private void HandleClicked()
  {
    OnClicked.InvokeAsync();
  }
}

DynamicPage.razor:(更新 #1)

@page "/DynamicPage"

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

@code{
  List<JsonComponent> components = new();

  protected async override Task OnInitializedAsync()
  {
     var jsonComponentList = await Http.GetFromJsonAsync<List<JsonComponent>>("/data.json");  

     foreach (var item in jsonComponentList)
     {
        JsonComponent componentItem = new();
        componentItem.Type = Type.GetType($"{nameSpaceComponents}{item.Type}");
        componentItem.Parameters = new();

        // below code to populate the component parameters as Dictionary<string, object>.
        // the problem here is how to pass "DoAction()" method which is defined in the 
        // Json file to the rendered component by adding it to the paramater Dictionary<string, object>?

       foreach (var kvp in item.Parameters)
       {
         var jsonElement = ((JsonElement)kvp.Value).GetString();
         componentItem.Parameters.Add(kvp.Key, jsonElement);  
       }
     }
  }  

  public void DoAction()
  {
     //.. codes to perform some custom logic here when Button component is clicked.
  }

}

JsonComponent.cs Class:

public class JsonComponent
{
  public JsonComponent()
  {
  }

  public Type Type { get; set; }
  public Dictionary<string, object> Parameters { get; set; }
}

为了在运行时通过Dictionary 参数将方法或函数传递给动态生成的Component,我们需要使用EventCallback.Factory.Create()方法创建一个EventCallBack。

我使用 foreach 循环获取 Json 文件中定义的参数列表,我们需要将其添加到 Dictionary 参数中。

然后我有一个if条件来检查要传递给Component的参数是否是一个Event。在这里,我预定义了所有事件键名称应该以 'On' 作为前缀,例如'OnClicked'。如果参数是一个事件,那么我们创建一个 EventCallback

    callback = EventCallBack.Factory.Create<string>(this, (Action<string>) this.GetType().GetMethod(kvp.Value.ToString()).CreateDelegate(typeof(Action<string>), this));
    componentItem.Parameters.Add(kvp.Key, callback);

下面是 DynamicPage.razor 的代码:

@page "/DynamicPage"

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

@code{
  List<JsonComponent> components = new();
  EventCallBack<string> callback;  // Declare a EventCallBack variable use to pass to the generated Component

  protected async override Task OnInitializedAsync()
  {
     var jsonComponentList = await Http.GetFromJsonAsync<List<JsonComponent>>("/data.json");  
   
     foreach (var item in jsonComponentList)
     {
       JsonComponent componentItem = new();
       componentItem.Type = Type.GetType($"{nameSpaceComponents}{item.Type}");
       componentItem.Parameters = new();
        
       foreach (var kvp in item.Parameters)
       {
         var jsonElement = ((JsonElement)kvp.Value).GetString();
         if (kvp.Key.ToString().StartsWith("On"))
         {
           callback = EventCallBack.Factory.Create<string>(this, (Action<string>) this.GetType().GetMethod(kvp.Value.ToString()).CreateDelegate(typeof(Action<string>), this));
           componentItem.Parameters.Add(kvp.Key, callback);
         }
         else
         {
           componentItem.Parameters.Add(kvp.Key, jsonElement);  
         }
       }
       components.Add(componentItem);
     }
  }
}