I would like to do in Blazor something that I normally do in React: create a reusable component that internally uses other child components, with the ability to pass those child components as parameters. I need that to be able to treat child components as a dependency that can be injected on demand with any custom implementation that could be needed in different contexts.
Imagine, for instance, a TextBox.razor
component that gives you the ability to pass a custom component to render the label as you want, as long as it implements an ILabel
interface. I tried something like this but the syntax doesn't seem to be valid:
As you see from the screenshot, Blazor doesn't allow me to use the Parameter Label
as a component. Any idea of how to achieve this?
You should be able to accomplish this with templated components.
Textbox.razor
@typeparam inputType
<div class="textbox">
@if(LabelTemplate!=null && TItem!=null)
@LabelTemplate(TItem)
<input type="text"/>
</div>
@code{
[Parameter]
public RenderFragment<inputType> LabelTemplate { get; set; }
[Parameter]
public inputType TItem { get; set; }
}
In the code above, you are specifying that the component accepts a type using @typeparam inputType
and receive an object of that type as a parameter TItem
.
You are also accepting a LabelTemplate
which accepts an object of type inputType
. To render this fragment, we call @LabelTemplate
and pass in our TItem
parameter.
Now lets look at how to use our templated component in a new component called PersonForm.razor
PersonForm.razor
<Textbox TItem="myPerson">
<LabelTemplate>
@context.Name
</LabelTemplate>
</Textbox>
<Textbox TItem="myPerson">
<LabelTemplate>
@context.PhoneNumber
</LabelTemplate>
</Textbox>
@code{
Person myPerson = new Person { Name = "Jane Doe", PhoneNumber = "999 999 9999" };
public class Person
{
public string Name { get; set; }
public string PhoneNumber { get; set; }
}
}
I'm passing in my Person object to each Textbox component's TItem property, and accessing it in the LabelTemplate using the @context
syntax.
This might seem confusing at first, so please read up on it here
Edited It just depends on what you want to accomplish. With the Verbose syntax comes flexibility on the "implementation" side of the component. Instead of forcing an interface that might not work with a wide variety of models/classes, you are letting the implementation specify what to do.
If you want something less verbose/more rigid, you can do the following as well.
@implements ILabel
<div class="textbox">
<label>@Text</label>
<input type="text"/>
</div>
@code
{
[Parameter]
public string Text { get; set; }
}
ILabel.cs
public interface ILabel
{
string Text { get; set; }
}
I realize this is probably late, but I just struggled through this and found out it is SUPER easy! Thought I would put an easy answer out there for people looking.
Here is my OrdersNavigation.razor file (which I want to embed into a header):
<div class="nav-strip">
<NavLink href="orders">
<Icon Name="@Icons.Cart" /> List
</NavLink>
<NavLink href="orders/create">
<Icon Name="@Icons.Plus" /> Create
</NavLink>
</div>
Now here is my PageHeader.razor:
<div class="page-header">
<h3>@Title</h3>
@Navigation
</h3>
<hr />
@code {
[Parameter] public string Title { get; set; } = "[TITLE]";
[Parameter] public RenderFragment Navigation { get; set; }
}
Notice that the Navigation property is a RenderFragment - this is key. Now in my page, I can simply add it like this:
<PageHeader Title="Orders">
<Navigation>
<OrderNavigation />
</Navigation>
</PageHeader>
You see here that the Title parameter is entered like usual, but the Navigation parameter is entered as an element of PageHeader! Really, you can put anything in the tags and it will render where you have @Navigation.
Reference: https://blazor-university.com/templating-components-with-renderfragements/passing-data-to-a-renderfragement/
Took a shot at your example:
Label.razor
<label>@Text</label>
@code {
[Parameter] public RenderFragment Text { get; set; }
}
TextBox.razor
<div class="textbox">
<Label>
<Text>
<div>
Embedded label <br />
You can even drop components in here!
</div>
</Text>
</Label>
<input />
</div>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With