This content originally appeared on Telerik Blogs and was authored by Jon Hilton
You won’t always know which components you need to render ahead of time. Here’s how to render components dynamically in response to changing data.
Most of the time you know what components you need for your app at “design time.”
As you’re writing the code and markup for your site, you know you need a NavBar, so you create a NavBar component and render it on the page.
<div>
<NavBar />
</div>
This makes it very easy to look at your code and see which components are going to be rendered when you run it in the browser.
However, there are times you don’t necessarily know up front which components you need to show on the page.
Take for example, a customizable dashboard. You know the sort—a page that brings together lots of different information like graphs, tables, maybe a form or two.
The key word here is “customizable.” If you’re just building a fixed dashboard that looks the same for everyone, you can stick to the hardcoded approach we mentioned before.
But if users are to customize their own dashboards, you’re probably going to need a different approach.
Dynamic Dashboards
The requirement is to take some arbitrary components and render those on the page.
In this case, we can imagine that we might store a user’s custom dashboard configuration using a structure like this:
DashboardConfig.cs
public class DashboardConfig {
public List<Panel> Panels { get; set; }
}
public class Panel {
public string Title { get;set; }
}
We can then iterate over the list of panels in a Blazor component.
Dashboard\Index.razor
@foreach(var panel in dashboard.Panels){
<div>
<h1>
@panel.Title
</h1>
<div>
...
</div>
</div>
}
@code {
DashboardConfig dashboard = ...;
}
So far so good, but what about those custom components we mentioned?
DynamicComponent to the Rescue
Blazor, as of .NET 6, has a very handy DynamicComponent
component for this exact purpose:
<DynamicComponent Type="@typeof(Counter)"/>
So long as we know the type of the component we want to render, DynamicComponent
will do the rest.
Let’s update our Dashboard
model to include a component type:
DashboardConfig.cs
public class DashboardConfig {
public List<Panel> Panels { get; set; }
}
public class Panel {
public string Title { get;set; }
public Type Component { get;set; }
}
Now we can modify our markup to render this component using DynamicComponent
.
Index.razor (markup)
@foreach(var panel in dashboard.Panels){
<div>
<h1>
@panel.Title
</h1>
<div>
<DynamicComponent Type="@panel.Component" />
</div>
</div>
}
We can quickly test this by creating an instance of DashboardConfig
in code:
Index.razor (@code)
@code {
DashboardConfig dashboard = new DashboardConfig {
Panels = new List<Panel>{
new Panel { Title = "Counter", Component = typeof(Counter) },
new Panel { Title = "Weather", Component = typeof(FetchData) }
}};
}
Just like that, we have a dashboard!
At this point, you might want to swap out your dashboard panel markup for a better looking component. If you’re using a component library like Telerik UI for Blazor, it’s probably worth taking a moment to find a component which you can drop in to act as a dashboard panel.
If you’re using the default Blazor project templates, you can use Bootstrap’s card
classes to get to something that looks a little better in the browser:
@foreach (var panel in dashboard.Panels)
{
<div class="card my-4">
<h1 class="card-header">
@panel.Title
</h1>
<div class="card-body">
<div class="card-text">
<DynamicComponent Type="@panel.Component" />
</div>
</div>
</div>
}
Here’s how it looks in the browser:
Making the Dashboard a Little More Resilient
This is a good first step, but how would we actually store this dashboard config in a database?
For that we’ll need to store the name of the Type
(as we can’t store a Type directly in the DB).
Let’s start by modifying DashboardConfig
to use a string
for the component (instead of a Type
).
DashboardConfig.cs
public class DashboardConfig {
public List<Panel> Panels { get; set; }
}
public class Panel {
public string Title { get;set; }
public string Component { get;set; }
}
At this point, we’ll run into a compile error as we’re trying to assign a Type
to a string
when we create our test data. Let’s switch that code from typeof
to nameof
instead.
Index.razor (@code)
@code {
DashboardConfig dashboard = new DashboardConfig
{
Panels = new List<Panel>
{
new Panel { Title = "Counter", Component = nameof(Counter)},
new Panel { Title = "Weather", Component = nameof(FetchData)}
}
};
}
Now we’re simply grabbing the name of the component and assigning it (as a string
) to our panel’s component property.
But if we try to run this we’ll bump into a new error.
error CS1503: Argument 1: cannot convert from 'string' to 'System.Type'
The problem is we’re now passing the name of the component around, but DynamicComponent
wants a type.
We also need to handle the possibility that we’ll store the name of a component in the system only to find, at some later date, that said component has been renamed or deleted and can no longer be rendered.
Happily, we can solve both problems with a small tweak to our code.
First, in the @code
for our dashboard we can create a handy helper function to resolve the component type from a string.
Index.razor (@code)
@code {
...
private Type? ResolveComponent(string componentName)
{
return string.IsNullOrEmpty(componentName) ? null
: Type.GetType($"{App Namespace}.Widgets.{componentName}");
}
}
This assumes the components are located in a Widgets folder in the root of the app, and that we’ll replace {App Namespace}
with the actual namespace for the app.
Now we can use this function in our markup to render the component (only when we can resolve its type).
Index.razor (markup)
<div class="card my-4">
<h1 class="card-header">
@panel.Title
</h1>
<div class="card-body">
<div class="card-text">
@{
var componentType = ResolveComponent(@panel.Component);
if (componentType != null)
{
<DynamicComponent Type="componentType"/>
}
}
</div>
</div>
</div>
From here we can persist that configuration and load it again when a user attempts to access the dashboard.
Passing Data to Dynamic Components
You may also want to provide extra configuration for a component beyond just its type.
In this dashboard example, you can imagine users wanting to save extra parameters (like a date range or a specific product category for a widget that shows sales).
The challenge is to be able to store an arbitrary collection of data, then pass it along to the component when we render it.
DynamicComponent
allows for this by accepting a dictionary of parameters of type IDictionary<string, object>
.
Let’s update our DashboardConfig.Panel
model to store a dictionary of parameters.
DashboardConfig.cs
public class Panel
{
public string Title { get; set; }
public string Component { get; set; }
public Dictionary<string, object> Parameters { get; set; } = new();
}
Now we can use this Parameters
dictionary to store configuration for any of our dashboard components.
Index.razor (@code)
DashboardConfig dashboard = new DashboardConfig
{
Panels = new List<Panel>
{
new Panel
{
Title = "Counter",
Component = nameof(Counter)
},
new Panel
{
Title = "Weather",
Component = nameof(FetchData),
Parameters = new Dictionary<string, object> { ["StartDate"] = new DateTime(2022,2,1) }
}
}
};
FetchData
in this case accepts a StartDate
parameter.
Widgets\FetchData.razor
[Parameter]
public DateTime StartDate { get; set; } = DateTime.Now;
The last step is to pass these parameters along to DynamicComponent
.
<div class="card-text">
@{
var componentType = ResolveComponent(@panel.Component);
if (componentType != null)
{
<DynamicComponent Type="componentType" Parameters="@panel.Parameters"/>
}
}
</div>
From here we can persist that dashboard configuration to a database (or any other storage option) for each user and we have our user customizable dashboard!
Have you tried the Telerik REPL for Blazor? It’s a new web-based tool for the Blazor community that makes it quick and easy to write, run, save and share code snippets. Check it out!
This content originally appeared on Telerik Blogs and was authored by Jon Hilton
Jon Hilton | Sciencx (2022-03-10T13:52:02+00:00) How To Render Blazor Components Dynamically. Retrieved from https://www.scien.cx/2022/03/10/how-to-render-blazor-components-dynamically/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.