Data binding keeps a Blazor value and a C# property in step. For an HTML input, a property supplies the displayed value; a user change updates the property. Without a binding, that relationship takes both a value expression and a change-event handler. The @bind attribute expresses both directions in one place. section 33.4.2
Bind an input
In Bindings.razor, @bind="City" connects an input to a City property initialized to London. After editing the input, move focus away: its default change event then updates City and the displayed City value. Buttons that set City to Paris or Chicago demonstrate the reverse direction—the input displays the new property value. section 33.4.2
If waiting for the change event is too slow, use @bind-value="City" with @bind-value:event="oninput" to update as input occurs. DateTime bindings can also specify @bind:culture and @bind:format; a date input can bind directly to the same DateTime property. section 33.4.2
Listing 33.19 Specifying an event in the Bindings.razor file in the Blazor folder
<div class="form-group">
<label>City:</label>
<input class="form-control" @bind-value="City"
@bind-value:event="oninput" />
</div>
<div class="p-2 mb-2">City Value: @City</div>
<button class="btn btn-primary" @onclick="@(() => City = "Paris")">
Paris
</button>
<button class="btn btn-primary" @onclick="@(() => City = "Chicago")">
Chicago
</button>
@code {
public string? City { get; set; } = "London";
}
Shows the City binding configured to use oninput rather than the default change event. section 33.4.2
Binding also works across component boundaries. A child component must expose a value parameter and an EventCallback<T> parameter named by appending Changed to the value name. Apply [Parameter] to both. A parent can then use @bind-SelectedValue="SelectedCity" instead of wiring the value and callback separately. Selecting a city in the child changes the parent's highlighted table row; changing SelectedCity in the parent changes the child's selection. section 34.2.2
Listing 34.14 Using a custom binding in the PeopleList.razor file in the Blazor folder
<table class="table table-sm table-bordered table-striped">
<thead>
<tr>
<th>ID</th><th>Name</th><th>Dept</th><th>Location</th>
</tr>
</thead>
<tbody>
@foreach (Person p in People ?? Enumerable.Empty<Person>()) {
<tr class="@GetClass(p?.Location?.City)">
<td>@p?.PersonId</td>
<td>@p?.Surname, @p?.Firstname</td>
<td>@p?.Department?.Name</td>
<td>@p?.Location?.City, @p?.Location?.State</td>
</tr>
}
</tbody>
</table>
<SelectFilter values="@Cities" title="@SelectTitle"
@bind-SelectedValue="SelectedCity" />
<button class="btn btn-primary mt-2"
@onclick="@(() => SelectedCity = "Oakland")">
Change
</button>
@code {
[Inject]
public DataContext? Context { get; set; }
public IEnumerable<Person>? People => Context?.People
.Include(p => p.Department)
.Include(p => p.Location).Take(ItemCount);
public IEnumerable<string>? Cities =>
Context?.Locations.Select(l => l.City);
public string SelectedCity { get; set; } = string.Empty;
public string GetClass(string? city) =>
SelectedCity == city ? "bg-info text-white" : "";
[Parameter]
public int ItemCount { get; set; } = 4;
[Parameter]
public string? SelectTitle { get; set; }
//public void HandleCustom(string newValue) {
// SelectedCity = newValue;
//}
}
Shows the parent binding a city-selection component to SelectedCity and changing that property with a button. section 34.2.2
For an edit form, put the model in EditForm's Model attribute and bind each input component's Value to the appropriate model property: for example, @bind-Value="PersonData.Firstname" on InputText or @bind-Value="PersonData.DepartmentId" on InputNumber. Match the component to the property's type; a mismatch causes an error when entered input is parsed. EditForm supplies the foundation for validation. section 36.2
Listing 36.7 Using form components in the Editor.razor file in the Blazor/Forms folder
@page "/forms/edit/{id:long}"
@layout EmptyLayout
<h4 class="bg-primary text-center text-white p-2">Edit</h4>
<FormSpy PersonData="PersonData">
<EditForm Model="PersonData">
<div class="form-group">
<label>Person ID</label>
<InputNumber class="form-control"
@bind-Value="PersonData.PersonId" disabled />
</div>
<div class="form-group">
<label>Firstname</label>
<InputText class="form-control"
@bind-Value="PersonData.Firstname" />
</div>
<div class="form-group">
<label>Surname</label>
<InputText class="form-control"
@bind-Value="PersonData.Surname" />
</div>
<div class="form-group">
<label>Dept ID</label>
<InputNumber class="form-control"
@bind-Value="PersonData.DepartmentId" />
</div>
<div class="text-center">
<NavLink class="btn btn-secondary" href="/forms">
Back
</NavLink>
</div>
</EditForm>
</FormSpy>
@code {
[Inject]
public NavigationManager? NavManager { get; set; }
[Inject]
DataContext? Context { get; set; }
[Parameter]
public long Id { get; set; }
public Person PersonData { get; set; } = new();
protected async override Task OnParametersSetAsync() {
if (Context != null) {
PersonData = await Context.People.FindAsync(Id)
?? new Person();
}
}
}
Shows a routed editor binding InputText and InputNumber components to properties of an EditForm model. section 36.2




