In Blazor, passing parameters between components is a fundamental technique for component communication, particularly from parent to child. This tutorial will guide you through passing a parameter to another component and then receiving the update notification when the parameter changes. We will cover:
@bind-*
.@bind-*
.EventCallback
.[Parameter]
attribute:<div> <h3>Child</h3> <div>Value: @Value</div> </div> @code { [Parameter] public int Value { get; set; } }
<div> <h3>Parent</h3> <div>Value: @ParentValue</div> <button @onclick="ChangeRandomValue" type="button">Change Value</button> <Child Value="ParentValue" /> </div> @code { public int ParentValue { get; set; } = 0; public void ChangeRandomValue() { var random = new Random(); ParentValue = random.Next(0, 100); } }
Any changes from the parent component will affect the child component accordingly.
Developers can enforce the requirement of a parameter using the [EditorRequired]
attribute:
@code { [EditorRequired] [Parameter] public int Value { get; set; } }
If the parent component omits the Value
parameter when using the child component, Visual Studio displays a warning or error message during development.
When a parameter is passed from the parent to the child component, it actually creates 2 copies - one at the parent and one at the child component. Because they are different properties, any changes to the property in the child component will not reflect in the parent component unless a notification is made to the parent component. Consider the following example:
We have the following child component:
<div> <h3>Child</h3> <div>Value: @Value</div> <button @onclick="ChangeRandomValue" type="button">Change Value</button> </div> @code { [Parameter] public int Value { get; set; } public void ChangeRandomValue() { var random = new Random(); Value = random.Next(0, 100); } }
And the parent component:
<div> <h3>Parent</h3> <div>Value: @ParentValue</div> <button @onclick="ChangeRandomValue" type="button">Change Value</button> <Child Value="ParentValue" /> </div> @code { public int ParentValue { get; set; } = 0; public void ChangeRandomValue() { var random = new Random(); ParentValue = random.Next(0, 100); } }
In this example, you can see both values at the parent and the child component. When the "Change Value" button in the child component is clicked, the value in the child component changes but not in the parent component.
To make the parent acknowledge the new value from the child component, a notification mechanism must be implemented. There are 2 mechanisms that provide the notification back to the parent: the bind
keyword and EventCallback
.
bind
keyword is the minimal code, and changes are notified automatically.EventCallback
requires more code setup but allows developers to notify changes when needed.@bind-*
To implement the binding mechanism, in the child component, declare an additional property with type EventCallback<T>
where T
is the data type of the property you want to notify. The name of this EventCallback<T>
must be the name of the property with the suffix Changed
. For example, if the property you want to notify is int Value
, then there must be a property EventCallback<int> ValueChanged
with the [Parameter]
attribute:
<div> <h3>Child</h3> <div>Value: @Value</div> <button @onclick="ChangeRandomValue" type="button">Change Value</button> </div> @code { [Parameter] public int Value { get; set; } [Parameter] public EventCallback<int> ValueChanged { get; set; } }
When the value changes in the child component, it is not required to change the property of the child component as it will cause overhead since the child component will be re-rendered by the parent component. Instead, just use the EventCallback<T>
to notify the parent about the changes:
public void ChangeRandomValue() { var random = new Random(); // Should not change the value directly in the child component. Notify the parent and let the child component render the new value // Value = random.Next(0, 100); // Notify the parent component about the change ValueChanged.InvokeAsync(random.Next(0, 100)); }
With this in mind, here is the full code of the child component:
<div> <h3>Child</h3> <div>Value: @Value</div> <button @onclick="ChangeRandomValue" type="button">Change Value</button> </div> @code { [Parameter] public int Value { get; set; } [Parameter] public EventCallback<int> ValueChanged { get; set; } public void ChangeRandomValue() { var random = new Random(); // Should not change the value directly in the child component. Notify the parent and let the child component render the new value // Value = random.Next(0, 100); // Notify the parent component about the change ValueChanged.InvokeAsync(random.Next(0, 100)); } }
The parent component uses @bind-<ParameterName>
to pass the parameter:
<div> <h3>Parent</h3> <div>Value: @ParentValue</div> <Child @bind-Value="ParentValue" /> </div> @code { public int ParentValue { get; set; } = 0; }
Note: With every parameter in the child component, you need an additional property declared. With this approach, each change from the child component will notify the parent component, even if there may still be changes from other properties of the child component. When it is costly to re-render the parent, consider using the EventCallback
approach to notify for the whole component’s changes instead of each property in the component.
@bind-*
When passing a parameter to the child component, developers can combine modifiers get
, set
, and after
with the bind
keyword to inject logic when getting the parameter value (get
), when setting the parameter value (set
), or after the parameter value has been set (after
). Consider the following changes count example:
<div> <h3>Parent</h3> <div>Value: @_parentValue</div> <div>Changes count: @ChangesCount</div> <Child @bind-Value:get="_parentValue" /> <Child @bind-Value:get="_parentValue" @bind-Value:set="SetParentValue" /> <Child @bind-Value:get="_parentValue" @bind-Value:set="SetParentValueAsync" /> <Child @bind-Value:get="_parentValue" @bind-Value:after="AfterSetParentValue" /> <Child @bind-Value:get="_parentValue" @bind-Value:after="AfterSetParentValueAsync" /> </div> @code { private int _parentValue = 0; public int ChangesCount { get; set; } = 0; public void SetParentValue(int newValue) { Console.WriteLine("SetParentValue executed"); _parentValue = newValue; ChangesCount++; } public async Task SetParentValueAsync(int newValue) { await Task.Delay(1000); // Simulate some async work Console.WriteLine("SetParentValueAsync executed"); _parentValue = newValue; ChangesCount++; } public void AfterSetParentValue() { Console.WriteLine("AfterSetParentValue executed"); _parentValue = _parentValue * 100; ChangesCount++; } public async Task AfterSetParentValueAsync() { await Task.Delay(1000); // Simulate some async work Console.WriteLine("AfterSetParentValueAsync executed"); _parentValue = _parentValue * 100; ChangesCount++; } }
Note: When using asynchronous logic in binding modifiers like@bind-Value:set
or@bind-Value:after
(e.g.,SetParentValueAsync
orAfterSetParentValueAsync
with a 1 second delay), a lock-screen feature should be implemented. This is critical because the UI may update without user interaction after a delay, potentially confusing users, as demonstrated in the example.
EventCallback
Notifying a parent component of multiple parameter changes in a child can lead to performance issues if done per parameter, as each notification triggers a re-render. Using a single EventCallback to handle all changes at once is a more efficient approach.
public record ChildComponentChangeArgument(int Value1, int Value2, int Value3);
EventCallback<ChildComponentChangeArgument>
in the child component.@code { [Parameter] public int Value1 { get; set; } [Parameter] public int Value2 { get; set; } [Parameter] public int Value3 { get; set; } [Parameter] public EventCallback<ChildComponentChangeArgument> ComponentUpdated { get; set; } }
EventCallback<T>
to notify the parent component:public void ChangeRandomValue() { var random = new Random(); int newValue1 = random.Next(0, 100); int newValue2 = random.Next(0, 100); int newValue3 = random.Next(0, 100); // Notify the parent component about the change ComponentUpdated.InvokeAsync(new(newValue1, newValue2, newValue3)); }
With that said, this is the full code of the child component:
<div> <h3>Child</h3> <div>Value1: @Value1</div> <div>Value2: @Value2</div> <div>Value3: @Value3</div> <button @onclick="ChangeRandomValue" type="button">Change Value</button> </div> @code { [Parameter] public int Value1 { get; set; } [Parameter] public int Value2 { get; set; } [Parameter] public int Value3 { get; set; } [Parameter] public EventCallback<ChildComponentChangeArgument> ComponentUpdated { get; set; } public void ChangeRandomValue() { var random = new Random(); int newValue1 = random.Next(0, 100); int newValue2 = random.Next(0, 100); int newValue3 = random.Next(0, 100); // Notify the parent component about the change ComponentUpdated.InvokeAsync(new(newValue1, newValue2, newValue3)); } }
<div> <h3>Parent</h3> <div>Value1: @ParentValue1</div> <div>Value2: @ParentValue2</div> <div>Value3: @ParentValue3</div> <Child Value1="ParentValue1" Value2="ParentValue2" Value3="ParentValue3" ComponentUpdated="OnComponentChanged" /> </div> @code { public int ParentValue1 { get; set; } = 0; public int ParentValue2 { get; set; } = 0; public int ParentValue3 { get; set; } = 0; public void OnComponentChanged(ChildComponentChangeArgument argument) { ParentValue1 = argument.Value1; ParentValue2 = argument.Value2; ParentValue3 = argument.Value3; } }