Better Way To Use CascadingParameters In Blazor

Introduction

So if you folks remember, last time we figured out the basic working of CascadingParameters in Blazor. Which was great!! In order to use CascadingParameters there are 2 rules to be followed. That's it.

  1. Name of the CascadingParameter really doesn't matter
  2. What matters is the type of the parameter

For eg., if the parent is sending a string parameter then the child should have a CascadingParameter which is of a type string.

Now, parameters being "type-dependent" rather than "name-dependent" doesn't really sound like a good deal, right? 

Because the problem with being "type-dependent" is the scope. Their scope is limited. In C#, we only have 15 built-in value types and 3 reference types. On the flip side, if parameters are "name-dependent" then you can send "n" number of parameters, there is absolutely no limit. 

So let's see how to make CascadingParameters "name-dependent" rather than "type-dependant".

We'll take the same example from last time, so I strongly recommend reading this article.

The child-component whose CascadingParameter's type matches with the parent's CascadingValue's type will get the values passed on, if the type is different then it will simply be ignored by the parent.

i.e. parent broadcasts all the CascadingValues to all of its descendants and then every descendant component who subscribes to that type specific CascadingParameter will receive the value.
In the following image 1,

  • Parent component is broadcasting 2 CascadingParameters 1. string type and 2. double
  • Child component is only has string type CascadingParameter
  • GrandChild is subscribing for both types string and double
  • Greatgrandchild is subscribing for string parameter only (same as child)


Image 1: Only GrandChild is subscribing for both type of CascadingParameters 

Let's code this out!

First, Parent component, having 2 parameters at line number 16 & 19 respectively and at line number 3 & 4 there are 2 CascadingValue tags which will broadcast these parameters to child components.

@page "/Parent"
<h5 class="box" style="width:450px;padding:5px;">Parent, sending string & double to descendants</h5>
<CascadingValue Value="@FirstMessage">
    <CascadingValue Value="@Money">
            <Child></Child>
    </CascadingValue>
</CascadingValue>
<style>
    .box {
        background-color: rgb(224, 206, 247);
        border: 5px solid rebeccapurple;
    }
</style>
@code {
    //First CascadingParameter 
    public string FirstMessage { get; set; } = "Hi from Parent!";

    //Second CascadingParameter 
    public double Money { get; set; } = 20000;
}

Listing 1: Parent.razor

Below is the child component, receiving only the string type and displaying it in the <h6> element.

<div class="boxChild">
    <h5 class="m-1">Child</h5>
    <h6>string: @Message</h6>
</div>
<GrandChild></GrandChild>
<style>
    .boxChild {
        background-color: #B7E5DD;
        border: 5px solid #123524;
        width: 400px;
        margin-left: 30px;
        margin-bottom: 10px;
        padding: 5px;
    }
</style>
@code {
    [CascadingParameter]
    public string Message { get; set; }
}

Listing 2: Child.razor

Next, grandchild subscribes to both types by creating 2 CascadingParameters one for each and showing it in <h5> & <h6> elements.

<div class="boxGrandChild">
    <h5 class="m-1">GrandChild</h5>
    <h6>string: @MessageDifferentName</h6>  
    <h6>double: @Money</h6>   
</div>
<GreatGrandChild></GreatGrandChild>
<style>
    .boxGrandChild {
        background-color: #B4C6A6;
        border: 5px solid #293462;
        width: 360px;
        margin-left: 50px;
        margin-bottom: 10px;
        padding: 5px;
    }
</style>
@code {
    [CascadingParameter]
    public string MessageDifferentName { get; set; }

    [CascadingParameter]
    public double Money { get; set; }
}

Listing 3: GrandChild.razor

Lastly, The great-grandchild is the same as a child.

<div class="boxGreatGrandChild">
    <h5 class="m-1">Great GrandChild</h5>
    <h6>string: @Message</h6>
</div>
<style>
    .boxGreatGrandChild {
        background-color: #F9CEEE;
        border: 5px solid #9A0680;
        width: 330px;
        margin-left: 60px;
        margin-bottom: 10px;
        padding: 5px;
    }
</style>
@code {
    [CascadingParameter]
    public string Message { get; set; }
}

Listing 4: GreatGrandChild.razor

The Problem

What if I want to send 2 string parameters instead of just one? Well, let's try that. Now we are only going to play with parent and child components, the same rules go for all descendants so we can ignore grandchild and great-grandchild components for next examples.

I have modified the parent component in listing 5, now we have 2 string messages going from parent, and 2 nested CascadingValues each for every message at line number 3 & 4.

@page "/Parent"
<h5 class="box" style="width:450px;padding:5px;">Parent, sending string & string to child</h5>
<CascadingValue Value="@FirstMessage">
    <CascadingValue Value="@SecondMessage">
            <Child></Child>
    </CascadingValue>
</CascadingValue>
<style>
    .box {
        background-color: rgb(224, 206, 247);
        border: 5px solid rebeccapurple;
    }
</style>
@code {
    //First CascadingParameter 
    public string FirstMessage { get; set; } = "First msg: Hi from Parent!";

    //Second CascadingParameter 
    public string SecondMessage { get; set; } = "Second msg: Hello again from Parent!";
}

Listing 5: Parent.razor with 2 string CascadingValues

To work with listing 5, I have made necessary changes in the child component as well, we have 2 CascadingParameters in the child component both string types.

<div class="boxChild">
    <h5 class="m-1">Child</h5>
    <h6>string: @FirstMessageFromParent</h6>
    <h6>string: @SecondMessageFromParent</h6>
</div>
<style>
    .boxChild {
        background-color: #B7E5DD;
        border: 5px solid #123524;
        width: 400px;
        margin-left: 30px;
        margin-bottom: 10px;
        padding: 5px;
    }
</style>
@code {
    [CascadingParameter]
    public string FirstMessageFromParent { get; set; }

    [CascadingParameter]
    public string SecondMessageFromParent { get; set; }
}

Listing 5: Child.razor with 2 string CascadingParameters

Following image 2 is the result, and it's not quite what we were looking, is it?


Image 2: Child is printing second message twice.

Explanation of image 2

Child is printing the second message twice. Why did that happen? Because when DOM starts executing, it first reaches to line number 3 in listing 4 gets the value for first string message then it reaches to line number 4, It knows it's the same type (string) so it overrides it with latest value and then at line number 5 it sends that value to child component. Now inside child component in listing 5, both CascadingParameters will receive the same value, "the latest overridden value".

The solution

To make this work all we have to use is the "name" for each attribute. Name is nothing but the key which acts as a unique identifier. At line number 3 & 4, you can see we are specifying names to each instance of CascadingValue.

@page "/Parent"
<h5 class="box" style="width:450px;padding:5px;">Parent, sending string & string to child</h5>
<CascadingValue Value="@FirstMessage" Name="First">
    <CascadingValue Value="@SecondMessage" Name="Second">
            <Child></Child>
    </CascadingValue>
</CascadingValue>
<style>
    .box {
        background-color: rgb(224, 206, 247);
        border: 5px solid rebeccapurple;
    }
</style>
@code {
    //First CascadingParameter 
    public string FirstMessage { get; set; } = "First msg: Hi from Parent!";

    //Second CascadingParameter 
    public string SecondMessage { get; set; } = "Second msg: Hello again from Parent!";
}

Listing 6: Parent.razor with 2 string CascadingValues associated with names

To match these names we have to do small changes in the child component as well. In listing 7, at line number 18 & 21, you would notice now we are using "named parameters" for the CascadingParameter attribute.

<div class="boxChild">
    <h5 class="m-1">Child</h5>
    <h6>string: @FirstMessageFromParent</h6>
    <h6>string: @SecondMessageFromParent</h6>
</div>
<style>
    .boxChild {
        background-color: #B7E5DD;
        border: 5px solid #123524;
        width: 400px;
        margin-left: 30px;
        margin-bottom: 10px;
        padding: 5px;
    }
</style>
@code {
    [CascadingParameter(Name = "First")]
    public string FirstMessageFromParent { get; set; }

    [CascadingParameter(Name = "Second")]
    public string SecondMessageFromParent { get; set; }
}

Listing 7: Child.razor with 2 string CascadingParameters associated with names.

If I run the project now, you'd see it working in image 3.


Image 3: Child is printing both the first and second message


And that's how you roll.

Before I end this topic, let me leave you with few rules to go with.

  • Make sure you name is exactly same in both parent and child components
  • If you mismatch the name then child component will print the default value of the type
  • Name is case-insensitive

Conclusion

This is how you can use name as key to send multiple parameters the same type and if you don't it will fall back on "type-dependent" binding. So rather than explicitly converting your types just use key identifiers. There's always a better solution to deal with every scenario in programming. Let's meet for our next adventure in Blazor land.

Hit me up @ Linkedin