Tutorial: Form Controls — Blazor
Compose a complete data-entry form using IsabyteTextInput,
IsabyteDropdown, IsabyteDateSelector, and IsabyteSwitch. Covers
two-way binding, validation, and submission — about 60 lines of Razor.
Package: Isabyte.PowerTools.Blazor (see Package Setup).
Step 1 — Define the Form Model
Isabyte controls bind to plain C# properties — there's no special model
base class. Add DataAnnotations attributes for validation; EditForm and
Blazor's built-in validator components pick them up automatically.
using System.ComponentModel.DataAnnotations;
public class SignUpForm
{
[Required, StringLength(60, MinimumLength = 2)]
public string FullName { get; set; } = "";
[Required, EmailAddress]
public string Email { get; set; } = "";
[Required]
public string Role { get; set; } = "";
[Required]
public DateTime? StartDate { get; set; }
public bool SubscribeToNewsletter { get; set; } = true;
}
Step 2 — Wire Up the Controls (text-input, dropdown, date-selector, switch)
Wrap the inputs in an EditForm. Its Model attribute is what
DataAnnotationsValidator inspects on submit, and OnValidSubmit fires
only when every annotation passes. Isabyte controls use the standard
Blazor two-way binding pattern — @bind-Value for any control exposing a
Value/ValueChanged pair — every control here except
IsabyteDateRangeSelector, which binds StartDate/EndDate instead.
@page "/sign-up"
<EditForm Model="_form" OnValidSubmit="HandleSubmitAsync">
<DataAnnotationsValidator />
<div class="mb-3">
<label class="form-label">Full name</label>
<IsabyteTextInput @bind-Value="_form.FullName" Placeholder="Jane Smith" />
<ValidationMessage For="@(() => _form.FullName)" />
</div>
<div class="mb-3">
<label class="form-label">Email</label>
<IsabyteTextInput @bind-Value="_form.Email"
Placeholder="jane@example.com"
Validation="TextValidation.EmailValidation" />
<ValidationMessage For="@(() => _form.Email)" />
</div>
<div class="mb-3">
<label class="form-label">Role</label>
<IsabyteDropdown @bind-Value="_form.Role"
Items="_roles"
Placeholder="Pick a role…" />
<ValidationMessage For="@(() => _form.Role)" />
</div>
<div class="mb-3">
<label class="form-label">Start date</label>
<IsabyteDateSelector @bind-Value="_form.StartDate" Min="DateTime.Today" />
<ValidationMessage For="@(() => _form.StartDate)" />
</div>
<div class="mb-3">
<IsabyteSwitch @bind-Value="_form.SubscribeToNewsletter"
Label="Send me product updates" />
</div>
<button type="submit" class="btn btn-primary">Create account</button>
</EditForm>
@code {
private SignUpForm _form = new();
private List<string> _roles = new() { "Developer", "Designer", "Product Manager", "Other" };
private async Task HandleSubmitAsync()
{
await _accountService.CreateAccountAsync(_form);
_navigationManager.NavigateTo("/welcome");
}
}
Step 3 — How the Binding Works
@bind-Value="_form.FullName" expands to two attributes at compile time:
it sets Value="_form.FullName" on the way in, and subscribes to
ValueChanged to assign back on the way out — identical to the binding
model used by built-in Blazor inputs like InputText. Reassigning the
field re-renders the control with the new value. (The control does keep a
little internal state — it tracks the last value it pushed so an echo from
the browser cannot fight your model — but you never manage that yourself.)
Step 4 — Validation
IsabyteTextInput.Validation gives per-field visual feedback while
typing (TextValidation.EmailValidation, TextValidation.PasswordValidation).
It doesn't replace DataAnnotationsValidator — EditForm +
[Required] still runs at submit time, producing the messages shown by
<ValidationMessage>.
Step 5 — Handle Submission
OnValidSubmit only fires once every annotation has passed; if anything
fails, OnInvalidSubmit fires instead. Return Task from the handler so
Blazor re-renders before and after the await — useful for a "Saving…"
button label wired to an _isSaving flag.
Common Gotchas
- Nullable mismatch.
IsabyteDateSelector.ValueisDateTime?. If your model field is non-nullableDateTime, the compiler accepts the binding but you lose the "no date selected" state. - Dropdown items late. If
Itemsfills from an async load, the dropdown renders empty until it completes. - Switch default value. A
booldefaults tofalse— initialize the field totrueif "opted in" is your preferred default.