24
- June
2020
Posted By : Rahul
Arbitrary Parameters and Attribute Splatting in Blazor

In this post, we will discuss about Arbitrary Parameters and Attribute Splatting in blazor in detail.

As you already know, Blazor is a component based UI framework. It means each page, each form or each block can be a component in blazor. Since the component is a reusable element , so the blazor components can also be nested. Meaning a component in blazor can be nested inside other blazor components.

We have described blazor components in detail in my previous article Deep Dive on Blazor Components. If you have not gone through it yet, please read it first.

Before explaining about arbitrary parameters and attribute splatting in blazor, it is necessary to understand about how blazor components can be nested and how a parent component can pass any data to its child components using [Parameter] attribute. For understanding in detail, you can check it here.

You can download demo project with examples explained from GitHub.

Let’s quickly recap about component parameter here in this post also with the help of an example.

Passing data from parent component to child component using [Parameter] attribute

A parent component in blazor can pass data to its child components using [Parameter] attribute. In the following example, parent component (Index.razor) can pass placeholder property value to its child component (DisplayName.razor).

Index.razor (parent component)

<!-- Parent Component -->

<h3>Blazor Parameters Demo</h3>

<DisplayName PlaceHolderValue="Enter Your Name" />

DisplayName.razor (child component)

<div class="form-group">
    <label for="txtName">Name:</label>
    <input type="text" class="form-control" id="txtName" placeholder="@PlaceHolderValue">
</div>

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

As you can in above code example, a property  named PlaceHolderValue is used decorated with [Parameter] attribute to pass placeholder attribute value from parent component to child component.

Attribute Splatting

Using Attribute Splatting, a component can accept additional attributes in additional to the explicitly declared parameters and use them for additional attributes of an element. Additional attributes are captured by a Dictionary<string, object> and then splatted onto an html element using a special directive called @attributes, when component is rendered on the page.

Dictionary key is of type as string and value of type as object. It means that attributes that we want to set over html elements, would be stored as key in dictionary and attribute’s values would be stored as object.

attribute dictionary blazor

This is very useful when you have to pass multiple parameters value from parent component to child component and child component supports a lot of customization based on passed parameters.

In above example, child component (DisplayName.razor) expects only one parameter. What if your child component expects a large number of attributes.

Let’s understand it with an example. In the following example, we created a Dictionary<string, object> in child component for accepting attributes passed from parent component and also created a dictionary with same number of attributes in parent component which would be applied to child <input> element.

Index.razor (parent component)

<h3>Blazor Attribute Splatting Demo</h3>

<DisplayNameWithAttributeSplatting ArbitraryAttributes="attributesFromParent"></DisplayNameWithAttributeSplatting>

@code{
    public Dictionary<string, object> attributesFromParent { get; set; } = new Dictionary<string, object>()
    {
        { "id", "txtName" },
        { "name", "txtName" },
        { "placeholder", "Type Your name - from Parent" },
        { "maxlength", 10}
    };
}

DisplayNameWithAttributeSplatting.razor (child component)

<div class="form-group">
    <label>Name :</label>
    <input type="text" class="form-control" @attributes="ArbitraryAttributes">
</div>

@code {
    [Parameter]
    public Dictionary<string, object> ArbitraryAttributes { get; set; } = new Dictionary<string, object>()
    {
        { "id", "txtName" },
        { "name", "txtName" },
        { "placeholder", "Type Your name - from Child" },
        { "maxlength", 5}
    };
}

Let’s run the application and see the output.

attribute splatting example

Default Attribute Values

As we saw in above example, since we are passing attribute’s values from parent, so child component is applying those attributes and its values to the <input> element using Attribute Splatting.  Also we have defined a dictionary in child component with same attributes and assigned them some default values., which means these default values would be assigned to the attributes if not being passed from parent component.

Suppose in case you don’t pass placeholder attribute value from parent, default value assigned to placeholder attribute in child component will be applied to the <input> element placeholder attribute.

We can pass as much as attributes from parent using this dictionary. In the following example, we are passing another attribute named newattribute, which is not defined in child component dictionary with default value. This will be applied on child component <input> element.

default value of arbitrary attributes

Attribute Splatting with Explicit Parameters

This is what you just saw in above example is called Attribute Splatting. Even we can define explicit parameters (to child component inside index.razor) to pass more attribute values from parent component. In the following example, we will declare a parameter explicitly named Value.

Index.razor (parent component)

<h3>Blazor Attribute Splatting with Explicit Parameter</h3>

<DisplayNameWithAttributeSplatting ArbitraryAttributes="attributesFromParent" Value="Rahul"></DisplayNameWithAttributeSplatting>

DisplayNameWithAttributeSplatting.razor (child component)

<div class="form-group">
    <label>Name :</label>
    <input type="text" class="form-control" @attributes="ArbitraryAttributes" @bind="Value">
</div>

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

Let’s run the application and see the output.

blazor attribute splatting with explicit parameters

Arbitrary Parameters

As we saw in Attribute Splatting example, we can pass as much as attributes to child component using @attributes dictionary and you can do that very easily by adding new attributes to the dictionary declared inside parent component as we have already seen too in above example.

What if you want to pass arbitrary parameters just not by adding in dictionary, but want to pass directly through the child component being used inside parent component as we do generally with normal parameters.

In the following example, we are passing a attribute called “required” and its value (required=”true”) as an arbitrary parameter through child component itself and remember, we don’t have any explicit parameter with this same name in child component.

Index.razor (parent component)

<h3>Blazor Attribute Splatting with Arbitrary Parameters</h3>

<DisplayNameWithArbitraryParameters ArbitraryAttributes="attributesFromParent" required="true" />

@code{
    public Dictionary<string, object> attributesFromParent { get; set; } = new Dictionary<string, object>()
    {
        { "id", "txtName" },
        { "name", "txtName" },
        { "placeholder", "Type Your name - from Parent" },
        { "maxlength", 10},
        { "newattribute", "newvalue"}
    };
}

Let’s run the application now and see the output.

arbitrary parameters with non-matching property exception

You can see an exception occurs with the following error description –

“Unhandled exception rendering component: Object of type ‘ArbitraryParametersDemo.Pages.DisplayNameWithArbitraryParameters’ does not have a property matching the name ‘required'”

Blazor is complaining about you are passing an attribute to child component for which parameter to capture it does not exist in child component. There are two solutions for fixing this problem –

Solution 1. An explicit parameter (decorated with [Parameter] attribute) named “required” must be declared inside child component. Like below –

[Parameter]

Public bool required { get; set; }

Solution 2. CaptureUnmatchedValues property of [Parameter] attribute declared over arbitrary attributes dictionary must be true. Like below –

Arbitrary Parameters with CapturedUnmatchedValues Blazor

CaptureUnmatchedValues

CaptureUnmatchedValues is a property of [Parameter] attribute that allows unmatched attributes being passed from parent component.

👉 Only one parameter can be defined with CaptureUnmatchedValues in a single component.

Let’s set this property as true and run the application.

CaptureUnmatchedValues exception Blazor

This time you get another exception with the following error description –

“Unhandled exception rendering component: The property ‘ArbitraryAttributes’ on component type ‘ArbitraryParametersDemo.Pages.DisplayNameWithArbitraryParameters’ cannot be set explicitly when also used to capture unmatched values.”

Blazor is complaining about you cannot set ArbitraryAttributes explicitly, if you have set CaptureUnmatchedValues as true inside parent component.

Attribute Splatting and Arbitrary Parameters - CaptureUnmatchedValues exception

Solution

The solution to fix this problem is that pass all attributes (declared in ArbitraryAttributes dictionary collection) to child component as explicit parameters and remove ArbitraryAttributes attribute from the child component.

CaptureUnmatchedValues exception solution

Index.razor (parent component)

<h3>Blazor Attribute Splatting with Arbitrary Parameters</h3>

<DisplayNameWithArbitraryParameters id="txtName" name="txtName" 
                                    placeholder="Type your name - from Parent" 
                                    maxlength="10" newattribute="newvalue" 
                                    required="true" />

Let’s run the application now and see the output.

Arbitrary Parameters CaptureUnmatchedValues exception solution

Position of @attributes directive

If you remember, we had assigned default values to the attributes in child component, just to assign default value to the attribute in case it is not passed from parent. An alternative to assign default value to the attribute of child component, we can define the attribute with default value in the child component element itself.

In the following example, the attribute placeholder is defined with default value “Enter your name – default” after @attributes directive in the child component and in the parent component, value for the same attribute has been passed through arbitrary parameter.

DisplayNameWithArbitraryParameters.razor (child component)

<div class="form-group">
    <label>Name :</label>
    <input type="text" class="form-control" @attributes="ArbitraryAttributes" placeholder="Enter your name - default">
</div>

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object> ArbitraryAttributes { get; set; } = new Dictionary<string, object>();
}

Index.razor (parent component)

<DisplayNameWithArbitraryParameters id="txtName" name="txtName" 
                                    placeholder="Type your name - from Parent" 
                                    maxlength="10" newattribute="newvalue" 
                                    required="true" />

What do you think? 😉 What should be the value of placeholder attribute when child component will be rendered? Let’s run the application and see the output.

Attribute Splatting and Arbitrary Parameters attributes position

You can see the output is “Enter your name – default“. It means child component default value overrides the value of arbitrary parameter placeholder value passed from parent component. This is because the parameter attribute in the child component has been used to the right side of @attributes directive that contains the arbitrary attributes.

Reason behind @attributes directive should always use right side of other parameters

This is to keep in mind that the @attributes directive always should use on the right side of each attribute of the element. The reason behind when @attributes directive is splatted on the element, the arbitrary attributes passed from the parent component are processed from right to left and this is the reason child component placeholder attribute did override the arbitrary placeholder attribute.

To fix this, let’s remove the @attributes directive and place it in the last after every attribute on the element (as shown in below image).

attributes directive position solution

👉 Remember, Arbitrary Parameters dictionary parameter always must have right side of other attributes of the element.

Let’s run the application after placing it in last.

attributes directive position output

Summary (Arbitrary Parameters and Attribute Splatting)

In this article, we have covered –

👉 How to pass data from parent component to child component using [Parameter] attribute in Blazor?
👉 What is Attribute Splatting and Arbitrary Parameters in Blazor?
👉 How to set default Attribute Values in Attribute Splatting in Blazor?
👉 Using Explicit Parameters with Attribute Splatting in Blazor.
👉 What is CaptureUnmatchedValues in Blazor?
👉 Why @attributes directive position always should be right side of other attributes of the element?

Thank you 🙏 for reading this article. Keep Reading, Keep Growing, Keep Sharing 😃


If you found this article helpful,

BMC logoBuy me a coffee

Follow Us For Latest Posts –

Leave a Reply