static void

Blazor Forms

@page "/useredit/{Id:int}"
@using BlazorApp.Shared
@inject HttpClient Http
@inject NavigationManager Navigation

<p>Editing user @Id</p>
<EditForm Model="_user" OnValidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <p>
        <label for="UserName" class="form-label">Name</label>
        <InputText id="UserName" @bind-Value="_user.Name" class="form-control"/>
    </p>
    <button type="submit" class="btn btn-primary">Submit</button>
</EditForm>

@code {
    [Parameter]
    public int Id { getset; }

    private User? _user = new();

    protected override async Task OnInitializedAsync()
    {
        _user = await Http.GetFromJsonAsync<User>($"User/{Id}");
    }

    private async Task HandleValidSubmit(EditContext arg)
    {
        await Http.PostAsJsonAsync("User", _user);
        Navigation.NavigateTo("/");
    }
}

Binding

<p>One way: <input value="@Name"></p>
<p>Two way (default onblur): <input @bind="Name"></p>
<p>Two way (specific property- which for input is default value): <input @bind-value="Name"></p>
<p>Custom event <input @bind="Name" @bind:event="oninput"></p>
<EditForm EditContext="editContext">
    <p>With blazor InputText component: <InputText @bind-Value="Name" /></p>
</EditForm>
<EditForm OnSubmit="Save" EditContext="_editContext">
    <p><InputNumber @bind-Value="_model.Count"/></p>
    <button type="submit" disabled="@_formInvalid">Submit</button>
</EditForm>
@code {
    private EditContext _editContext = default!;
    private bool _formInvalid = true;
    private CounterModel _model = default!;
    protected override void OnInitialized()
    {
        _model = new CounterModel();
        _editContext = new(_model);
        _editContext.OnFieldChanged += (___) =>
        {
            _formInvalid = !_editContext.Validate();
            StateHasChanged();
        };
    }

Event handling

Components have html-DOM type event handlers (which fire WASM/.net delegates).

<button @onclick="BtnClick">Click Me</button>
<input type="checkbox" @onclick="HandleCheck" />
<input type="checkbox" @onclick="@(e => MyString = "Checked")" />

You can use onclick:preventDefault and onclick:stopPropagation modifiers.

For a child component, you may want one or more events passed up to the parent. Add a parameter of type EventCallback or EventCallback<TValue>

Parent:

<UserList Users="UserList" 
          OnSelectedCallback="SelectedUser" 
          OnClickCallback="SelectedAll"></UserList>
<p>Selected @_selectedUser</p> @code {
    private int? _selectedUser;
    private void SelectedUser(int id)
    {
        _selectedUser = id;
    }

    private void SelectedAll(MouseEventArgs obj)
    {
        _selectedUser = 9999; //magic value
    }

Child

<button @onclick="OnClickCallback">Select All</button>
<br />
<ul>
    @for (var i = 0; i < Users.Count; i++)
    {
        //we have to use a for-loop with counter
        var user = Users[i];
        <li>
            <button @onclick="() => Trigger(user.Id)">Select</button> @user.Name
        </li>
    }
</ul>

@code {
    [Parameter]
    public IList<User> Users { getset; } = default!;

    //event for the Select All button click- pass it to parent
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { getset; }

    //event from the individual rows, but fired from the Trigger event below
    [Parameter]
    public EventCallback<int> OnSelectedCallback { getset; }

    //the private event called from the lambda with custom data
    private async Task Trigger(int userId)
    {
        await OnSelectedCallback.InvokeAsync(userId);
    }
}