Binding data is a fundamental task in single page applications (SPAs). At some point every application needs to either display data (e.g. labels) or receive data (e.g. forms).
While most SPA frameworks have similar concepts for data binding, either one way binding or two way binding, the way they work and are implemented varies widely. In this post, we’re going to have a good look at how one way and two way binding work in Blazor.
One Way Binding
One way bindings have a unidirectional flow, meaning that updates to the value only flow one way. A couple of examples of one way binding are rendering a label dynamically or dynamically outputting a CSS class name in markup.
One way bindings can be constant and not change, but often the value will have a reason to be updated. Otherwise it would probably be better to avoid using a bound value altogether and just type the value out directly.
In Blazor, when modifying a one way binding the application is going to be responsible for making the change. This could be in response to user action or event such as a button click. The point being, that the user will never be able to modify the value directly, hence one way binding.
Now we have an idea what one way binding is let’s take a look at some examples.
<h1>@Title</h1>
@code {
private string Title { get; set; } = "Hello, World!";
}
In the code above, we have a component which displays a heading. The contents of that heading, Title
, is a one way bound value. In order to bind one way values we use the @
symbol followed by the property, the field or even the method we want to bind too.
<h1>@Title</h1>
<button @onclick="UpdateTitle">Update Title</button>
@code {
private string Title { get; set; } = "Hello, World!";
private void UpdateTitle()
{
Title = "Hello, Blazor!";
}
}
In the first example the value is set and never changed, in this example we’ve added a method which updates the value of Title
when the button is clicked.
As we talked about previously, values can only be updated in one direction and here we can see that in action. When the buttons onclick
event is triggered the UpdateTitle
method is called and Title
property is updated to the new value. Executing event handlers in Blazor triggers a re-render which updates the UI.
One Way Binding Between Components
In the previous examples, we looked at one way binding inside of a component. But what if we want one way binding across components? Using our previous example, say we wanted to display the title of a parent component in a child component, how could we achieve this? By using component parameters.
<!-- Parent Component -->
<h1>@Title</h1>
<button @onclick="UpdateTitle">Update Title</button>
<ChildComponent ParentsTitle="Title" />
@code {
private string Title { get; set; } = "Hello, World!";
private void UpdateTitle()
{
Title = "Hello, Blazor!";
}
}
<!-- Child Component -->
<h2>Parent Title is: @ParentsTitle</h2>
@code {
[Parameter] public string ParentsTitle { get; set; }
}
In the example, the parent component is passing its title into the child component via the child components ParentsTitle
component parameter. When then components are first rendered the headings will be the following.
<!-- Parent Component -->
<h1>Hello, World!</h1>
<!-- Child Component -->
<h2>Parent Title is: Hello, World!</h2>
When the Update Title button is pressed then the output will become the following.
<!-- Parent Component -->
<h1>Hello, Blazor!</h1>
<!-- Child Component -->
<h2>Parent Title is: Hello, Blazor!</h2>
Similar to what happened with the earlier example inside a single component. The button click event calls the UpdateTitle
method and the Title
property is updated. Then the running of the event handler triggers a re-render of the parent component.
This also updates the Title
parameter passed to the child component. Updating the component parameter triggers a re-render of the child component, updating its UI with the new title.
Two way binding
Now we know all about one way binding, you could probably guess that two way bindings have a bidirectional flow. Allowing values to be updated from two directions.
The primary use case for two way binding is in forms, although it can be used anywhere that an application requires input from the user. The primary method of achieving two way binding in Blazor is to use the bind
attribute.
The Bind Attribute
The bind
attribute is a very versatile tool for binding in Blazor and has 3 different forms which allows developers to be very specific about how they want binding to occur.
@bind=Property
@bind-Value=Property
@bind-Value=Property
@bind-Value:event="onevent"
We’re going to look at each of these over the next few examples to see how they work.
Basic Two Way Binding
<h1>@Title</h1>
<input @bind="@Title" />
@code {
private string Title { get; set; } = "Hello, World!";
}
Continuing with our previous examples, we have added an input control which is two way bound to the Title
value using the bind
attribute. If you run this code you will notice that the value of Title
doesn’t actually update until you tab out of the input.
This is because under the covers bind
is actually setting the value
attribute of the input to Title
and setting up a onchange
handler which will update Title
when the input loses focus. We can see this if we look at lines 8 and 9 of the compiled components BuildRenderTree
method.
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
{
builder.OpenElement(0, "h1");
builder.AddContent(1, Title);
builder.CloseElement();
builder.AddMarkupContent(2, "\r\n\r\n");
builder.OpenElement(3, "input");
builder.AddAttribute(4, "value", Microsoft.AspNetCore.Components.BindMethods.GetValue(Title));
builder.AddAttribute(5, "onchange", Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => Title = __value, Title));
builder.CloseElement();
}
The bind
attribute understands different control types, for example, a checkbox does not use a value
attribute, it uses a checked
attribute. bind
knows this and will apply the correct attributes accordingly.
This is all good, but what if we want our Title
to update as we type and not just when the input loses focus? Well, we can do that using a more specific version of bind
.
Two Way Binding To A Specific Event
<h1>@Title</h1>
<input @bind-value="Title" @bind-value:event="oninput" />
@code {
private string Title { get; set; } = "Hello, World!";
}
We can specify what event the bind
attribute should use to handle updating the value. As we now know, by default this event is onchange
, but in the example above we’ve specified the oninput
event. This event is fired for each character typed so the Title
s value is updated continually.
Two Way Binding Between Components
To create two way binding between components we can once again take advantage of the bind
attribute. We also need to setup our components with a certain convention, let’s look at an example.
<h1>@Title</h1>
<ChildComponent @bind-ParentsTitle="Title" />
<button @onclick="UpdateTitle">Update Title</button>
@code {
private string Title { get; set; } = "Hello, World!";
private void UpdateTitle()
{
Title = "Hello, Blazor!";
}
}
<h2>Parent Title is: @ParentsTitle</h2>
<button @onclick="UpdateParentsTitle">Update Parents Title</button>
@code {
[Parameter] public string ParentsTitle { get; set; }
[Parameter] public EventCallback<string> ParentsTitleChanged { get; set; }
private async Task UpdateParentsTitle()
{
ParentsTitle = "Hello, From Child Component!";
await ParentsTitleChanged.InvokeAsync(ParentsTitle);
}
}
We’re adapting our previous example from one way binding and making it two way. There’s now a button on the child component which triggers a method to update the ParentsTitle
and invokes the ParentsTitleChanged
EventCallback. The parent component has also been updated to use bind-ParentTitle
when passing its Title
parameter to the child component.
If we run the above code we’re able to click the button on the parent or the button on the child and both components titles will be updated. So how does this work?
The two key factors here are the EventCallback on the child component and the use of the bind
attribute on the child component in the parent. By using this version of bind
in the parent, it’s the equivalent of writing this.
<ChildComponent @bind-ParentsTitle="Title" @bind-ParentsTitle:event="ParentsTitleChanged" />
By default Blazor will look for an event on the child component using the naming convention of {PropertyName}Changed. This allows us to use the version of bind
, only specifying the property name. It is important to note that the event property will need to be marked with the Parameter
attribute.
However, it’s also possible to use a completely different name for the EventCallback property, for example, ParentsTitleUpdated
. But in this case we would need to use the long form version of bind and specify the event name like so.
<ChildComponent @bind-ParentsTitle="Title" @bind-ParentsTitle:event="ParentsTitleUpdated" />
Summary
I think that brings us to a nice conclusion. We’ve had a fairly deep look into binding in Blazor covering one way and two way binding.
We looked at how we use one way binding inside of a component and how values are updated. Then moved on to one way binding between components. We then moved onto two way binding, both within a component and between components, using the bind
attribute. We looked at how we can use the various forms of the bind
attribute to specify which values and/or events to use when binding.