Blazor - How to create Components dynamically

27,903

Solution 1

For version 0.2 this is the answer from Steve Sanderson:

We'll implement nicer APIs to build RenderFragments in the future, but for now you can

@CreateDynamicComponent();
@functions {
    RenderFragment CreateDynamicComponent() => builder =>
    {
        builder.OpenComponent(0, typeof(SurveyPrompt));
        builder.AddAttribute(1, "Title", "Some title");
        builder.CloseComponent();
    };
}

Those are very low-level APIs (not even documented) so we hope not many people need to do this right now. Higher-level APIs for this will come later.

Found here

Solution 2

Judging by the comments on the accepted answer and on the original version of this answer I think there may be a bit of confusion around dynamically adding components. There are (at least) a couple of ways to achieve this (and a number of existing questions on this, e.g. here). It all depends on exactly what you mean by 'dynamically':

1) Using conditional statements in your Razor code

If what you're trying to achieve is simply to show or hide components based on some state in your data or model, then the 'normal' way to render a component dynamically is to use some sort of conditional code in your Razor view.

Simple conditional rendering

@if (_showCounter)
{
  <MyCounterComponent Count="@_count" />
}

@code {
  bool _showCounter = true;
  int _count;
}

Simple repeating data sets

For repeating sets of data, such as lists of items you can take advantage of Blazor's data binding.

Take the example of a page/component that shows a sales order and then has a child component for each sales order line. You might have code that looks like this on your razor page:

  @foreach (var salesOrderLine in _salesOrder.salesOrderlines)
  {
    <SalesOrderLine SOLine=@salesOrderLine />
  };

If you had a button that added another sales order line then you could simply add the new record to the _salesOrder model/view model in that buttons click event. Button clicks normally trigger a re-render, so the page should then automatically show an extra SalesOrderLine component (and if it doesn't you can use this.StateHasChanged(); to tell it things are different and cause a re-render)

Repeating data sets containing different data types (possibly polymorphic)

If your list contains different types you can use a switch statement to decide which type of component to render, e.g. (from this GitHub question):

<ul>
    @foreach (fruit in fruits)
    {
        switch(fruit)
        {
            case PearComponent p:
                <PearComponent ParameterOfSomeSort="p"></PearComponent>
                <li>Or render pears like this, if you want the li in it</li>
                break;
            case AppleComponent a:
                <AppleComponent></AppleComponent>
                break;
            case BananaComponent b:
                <BananaComponent></BananaComponent>
                break;
            case RaspberryComponent r:
                <RaspberryComponent></RaspberryComponent>
                break;
        }
    }
</ul>

2. Dynamic rendering using RenderFragment

There are some cases that can't be handled well using the Razor approach above. In those cases RenderFragment offers another way to dynamically render parts of a page.

Polymorphic lists

Where you have a truly polymorphic list (e.g. a list of objects that all implement the same interface or inherit from the same class) then this type of approach can be used from this GitHub post:

@page "/"

@foreach (CounterParent counter in components)
{
    RenderFragment renderFragment = (builder) => { builder.OpenComponent(0, counter.GetType()); builder.CloseComponent(); };
    <div>
        <div>Before the component</div>
        @renderFragment
        <div>Afterthe component</div>
    </div>
}

@code
{
    List<CounterParent> components = new List<CounterParent>() { new CounterParent(), new CounterChild() };
}

The Blazor team are considering improving how polymorphic lists are handled in Blazor:

mkArtakMSFT commented on 1 Oct 2019 Thanks for contacting us, @Joebeazelman. Using the switch approach you have will be our recommendation. We will consider doing something better in this area in the future.

Conclusion

The key point here (for those from an MVC background) is that there's no need to try and manually inject the new HTML into the DOM, or dynamically load a partial view, in the way you might of in MVC, Blazor will do that for you.

Despite the similarity in syntax between razor pages for MVC and those for Blazor, the Blazor model is conceptually much closer to something like React than it is to MVC, it's really important to understand there's something along the lines of a shadow-DOM in the background.

This page has some good pointers on data binding in Blazor.

Solution 3

Of course you could do something like this too utilizing the BuildRenderTree but often not recommended due to ability to mess it up. (Microsoft documentation)

public class CustomLabel: ComponentBase
{  
    [Parameter] public string Id { get; set; }
    [Parameter] public string? Label { get; set; }

    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        if (!string.IsNullOrEmpty(Label))
        {
            builder.OpenElement(1, "label");
            builder.AddAttribute(2, "for", Id);
            builder.AddAttribute(3, "class", "custom-css-classes-here");
            builder.AddContent(4, Label);
            builder.CloseElement();
        }
    }

    // another example: maybe you would have an "IsRequired" flag 
    // that would add an "*" after the label if set to true
}
Share:
27,903
Diemauerdk
Author by

Diemauerdk

Updated on October 26, 2021

Comments

  • Diemauerdk
    Diemauerdk over 2 years

    I want to test if it was possible to create Blazor components dynamically.

    I can't find any way to do this. I have experimented a bit with some dynamic content found on this link but have not ended up with any results.

  • OSP
    OSP almost 5 years
    "we hope not many people need to do this" - well any designer app has to have the ability to add components dynamically. WYSIWYG editors, process modelling UI, dashboarding UI...so it is not rare and extraterrestrial requirement...
  • ATL_DEV
    ATL_DEV over 4 years
    What?! Rendering components dynamically is incredibly useful and very common. How would you, for instance, handle a polymorphic list of components where every member implements its own unique visual presentation which can't be determined at compile time because they may be extermal plugin component? Your approach falls flat on its face in this scenario.
  • tomRedox
    tomRedox over 4 years
    Hi @ATL_DEV. It probably slightly depends on how broad or narrow the definition of 'dynamic' is, I've updated my answer to try and make that clearer. I certainly agree it's a very useful facility, but I wouldn't personally say it was that common, for example we've just written a large line of business app in Blazor and not used RenderFragment once. The scenario of polymorphic rendering of external plugins would definitely be an excellent use of RenderFragment though. The Blazor team are looking to improve polymorphic handling by the way, see the github link in my answer.
  • IvanH
    IvanH about 4 years
    Maybe it is low level, but it has documentation RenderTreeBuilder.OpenComponent Method