Component Interaction

Component is the building block of a web page. A component has its UI, the according logic and is also reusable. A web page will have many components to display different parts of the page. For that reason, the interaction between components is needed. The interaction contains sharing data and how to react when the data is changed. There are 4 ways to exchange data between components:

  1. Using CascadingParameter to pass parameters from one of the parent components to the child component. One-way notifying.
  2. Using Parameter to pass parameters from the from the closest parent component to the child component. One-way notifying.
  3. Combining Parameter and delegate to notify changes from the child component. Two-way notifying.
  4. Using transfer service technique to share information across many components regardless of their relations. Two-way notifying.
You can download the example code used in this topic on GitHub.

Passing data using CascadingParameter

Using CascadingParameter is a good way to share data from the grandparent or parent component to the descendant components. The following image illustrates how CascadingParameter works.

When using CascadingParameter to pass data, any data changes that occur in the descendants components will not notify the ancestor component but any data changes from the ancestor component will notify the descendant component.

Creating the ancestor component

  1. Create a new component and declare a public property.
public Person FirstPerson { get; set; } = new()
{
    Name = "Person from grand parent component"
};
  1. Use CascadingValue component to pass the value.
<CascadingValue Value="FirstPerson">
    <CPParent></CPParent>
</CascadingValue>

The CascadingValue component should wrap the parent component. Otherwise it cannot be passed.

Creating the descendant component

  1. Create a new component and declare a public property with CascadingParameter attribute.
[CascadingParameter]
public Person ReceivedPerson { get; set; }
  1. Use the property as other properties.
<span>Received value: @ReceivedPerson.Name</span>

The property of the descendant is not required to have the same property name. As in the example, you are passing FirstPerson from the ancestor component to ReceivedPerson in the descendant component. Instead, CascadingParameter in the descendant component will look for the same type from the ancestor component. The example code will not work if FirstPerson and ReceivedPerson are not the same type. Here is the result of the example.

Passing multiple CascadingParameter

The previous example demonstrates how to pass a single parameter using CascadingParameter. You can also pass multiple parameters using CascadingParameter, if you have 2 parameters with the same type, you will have to name that parameter. In the following example, you will pass another parameter with the Person type.

  1. Declare multiple parameters in the ancestor component.
public Person FirstPerson { get; set; } = new()
{
    Name = "Person from grand parent component"
};

public Person SecondPerson { get; set; } = new()
{
    Name = "Another person from grand parent component"
};
  1. Using nested CascadingValue to pass the parameters, you can change the order as you want. You will need to name the passing parameter by adding Name="<ParameterName>".
<CascadingValue Value="FirstPerson" Name="GrandParentFirstPerson">
    <CascadingValue Value="SecondPerson" Name="GrandParentSecondPerson">
        <CPParent></CPParent>
    </CascadingValue>
</CascadingValue>
  1. In your descendant component, declare a property with CascadingParameter attribute and pass the name you have specified at the previous step.
[CascadingParameter(Name = "GrandParentFirstPerson")]
public Person Received1stPerson { get; set; }
[CascadingParameter(Name = "GrandParentSecondPerson")]
public Person Received2ndPerson { get; set; }
You don't need to declare all the passed parameters in your descendant component, just declare what you need to use. For example, you can remove Received1stPerson from the descendant component.

Passing data using Parameter

Parametercomponent is a built-in attribute that allows you to pass data from the parent component to the child component.

This approach does not support feedback from the child component. Any changes from the child component will not notify the parent component. However, any changes that come from the parent component will notify the child component. The child component receives the property as a reference to the parent's property, if you make a change in the child component, the parent's property will have a new value but the UI will not show the old value until you call StateHasChanged() in your parent component.

Creating the child component

  1. Create a new component and declare a public property with Parameter attribute.
[Parameter]
public Person ReceivingPerson { get; set; }
  1. Use the property as a local property.
<div>Received Value: @ReceivingPerson.Name</div>

Creating the parent component

  1. Create a new component and declare a public property.
public Person PersonInParentComponent { get; set; } = new()
{
    Name = "Person from the parent component."
};
  1. Pass the property PersonInParentComponent to the child component.
<PChild ReceivingPerson="PersonInParentComponent"></PChild>

You can see the following result:

Common mistake

A common mistake when using the Parameter is to change the reference of the child property. This will break the bond between the child component and parent component. Normally, when the change happens inside the child component, the parent component gets the new value but the UI is not updated. Try to avoid changing the reference of the child property.

However, changing the reference when declaring the property is totally fine. The following code is totally fine.

[Parameter]
public Person ReceivingPerson { get; set; } = new()
{
    Name = "Default name"
};

Passing data using Parameter and delegate

This technique is preferred when you want to pass a parameter to a child component, process that parameter and then notify back to the parent component. The following image describes this technique:

  1. Create a child component and declare 2 parameters with Parameter attribute.
[Parameter]
public Person ReceivingPerson { get; set; }

[Parameter]
public Action OnReceivingPersonChanged { get; set; }

In this example, ReceivingPerson is the data that the parent component will pass and OnReceivingPersonChanged is the delegate to notify data changes.

You can use any kind of delegate (Action, Func, Predicate) as the parameter.

  1. Invoke the OnReceivingPersonChanged delegate when the data is changed to notify the parent to update its UI.
<form>
    <label>
        Receiving Person:
        <input type="text" value="@ReceivingPerson.Name" @oninput="ChangeOnReceivingPerson" />
    </label>
</form>

@code {
    ...

    private void ChangeOnReceivingPerson(ChangeEventArgs eventArgs)
    {
        ReceivingPerson.Name = eventArgs.Value as string;
        OnReceivingPersonChanged?.Invoke();
    }
}
  1. Create a parent component and declare a property and a method to update the UI.
public Person Person { get; set; } = new()
{
    Name = "Person from parent"
};

public void OnPersonChanged()
{
    StateHasChanged();
}
  1. Pass the property and the method to the child component.
<PDChild ReceivingPerson="Person" OnReceivingPersonChanged="OnPersonChanged"></PDChild>

And you are done.


Passing data using a transfer service

This technique can be applied to any situation, it is a great way to decoupling your code and share parameters between components and get a notification when the data is manipulated. The following image describes the transfer service.

Creating the model

The model contains properties, you can create an event and a delegate for each property or you can just implement the INotifyPropertyChanged interface. The difference is that by using INotifyPropertyChanged interface, you will have one event per class instead of one event for each property. In this section example, we will choose the first approach.

public class Game
{
    private string _name;

    public string Name
    {
        get => _name;
        set
        {
            _name = value;
            OnNameChanged?.Invoke(this, value);
        }
    }

    public delegate void OnNameChangedHandler(object sender, string name);
    public event OnNameChangedHandler OnNameChanged;
}

Creating the transfer service

The transfer service is a scoped service that will hold the data to transfer between the components.

public class GameTransferService
{
    public Game Game { get; set; } = new();
}

Registering the transfer service

After creating the service, head to the Program.cs to register the transfer service as a scoped service.

public static async Task Main(string[] args)
{
    ...
    builder.Services.AddScoped<GameTransferService>();
    ...
}

Using the transfer service in a component

To use the transfer service in a component, you need to have @inject directive to inject the transfer service and have the @implements to implement IDisposable interface. In the initialized phase, you will subscribe to the event of the Game model. You will use the property in the transfer service directly instead of creating a property in your component.

@inject GameTransferService GameTransferService
@implements IDisposable

<div>Shared Game Name: @GameTransferService.Game.Name</div>

@code {
    protected override void OnInitialized()
    {
        GameTransferService.Game.OnNameChanged += OnGameNameChanged;
    }

    private void OnPersonChanged(object sender, Person person)
    {
        StateHasChanged();
    }

    private void OnGameNameChanged(object sender, string name)
    {
        StateHasChanged();
    }

    public void Dispose()
    {
        GameTransferService.Game.OnNameChanged -= OnGameNameChanged;
    }
}

Always remember to unsubscribe from the event using the Dispose method of IDisposable interface.

And now with the help of transfer service, you can share the data between components easily.

BLAZOR SCHOOL
Designed and built with care by our dedicated team, with contributions from a supportive community. We strive to provide the best learning experience for our users.
Docs licensed CC-BY-SA-4.0
Copyright © 2021-2025 Blazor School
An unhandled error has occurred. Reload 🗙