Search code examples
c#asp.net-coreblazorblazor-webassemblyasp.net-blazor

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


I'm trying to render dynamic content in Blazor Wasm using DynamicComponent from .Net 6. The component to render is defined in a JSON file with the following structure.

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

DynamicComponent allows us to pass parameters to the rendered Component using Dictionary<string, object> like the code below where we can define component.parameters as Dictionary<string,object>

<DynamicComponent [email protected] [email protected] />

In my razor file, I have defined the "DoAction()" method. I want the this method to be called when the MyButton component is clicked. But how can we pass this DoAction() method as EventCallBack to the rendered Component?

MyButton.razor component:

<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: (Update #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; }
}

Solution

  • In order to pass the method or function at runtime to the dynamically generated Component via the Dictionary<string, object> Parameters, we need to create an EventCallBack by using EventCallback.Factory.Create() method.

    I use the foreach loop to get the list of parameters defined in the Json file that we need to add into the Dictionary<string, object> Parameters.

    Then I have an if condition to check whether the parameter to be passed to the Component is an Event. Here I predefined all Event Key name should prefix with 'On' e.g. 'OnClicked'. If the parameter is an Event then we create an 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);
    

    Below is the codes for the 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);
         }
      }
    }