EditForm – Demo đầy đủ (Blazor)
Cover: EditForm, DataAnnotations, ValidationSummary/ValidationMessage, OnValidSubmit/OnInvalidSubmit, EditContext, FieldChanged, Custom validation (ValidationMessageStore), Reset.
1️⃣ Basic EditForm – DataAnnotations + ValidationSummary
Status: none
Xem code
@page "/editform-demo"
<EditForm Model="@BasicModel"
OnValidSubmit="Basic_OnValidSubmit"
OnInvalidSubmit="Basic_OnInvalidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<InputText class="form-control" @bind-Value="BasicModel.Name" />
<ValidationMessage For="@(() => BasicModel.Name)" />
<InputText class="form-control" @bind-Value="BasicModel.Email" />
<ValidationMessage For="@(() => BasicModel.Email)" />
<button type="submit">Submit</button>
</EditForm>
@code {
private BasicUser BasicModel = new();
private string BasicStatus = "none";
private void Basic_OnValidSubmit()
=> BasicStatus = $"✅ Valid: {BasicModel.Name} - {BasicModel.Email}";
private void Basic_OnInvalidSubmit()
=> BasicStatus = "❌ Invalid submit";
private void Basic_Reset()
{
BasicModel = new();
BasicStatus = "reset";
}
public class BasicUser
{
[Required, StringLength(30)]
public string Name { get; set; } = "";
[Required, EmailAddress]
public string Email { get; set; } = "";
}
}
2️⃣ Nhiều kiểu input + OnValidSubmit / OnInvalidSubmit
Xem code
<EditForm Model="@FullModel"
OnValidSubmit="Full_OnValidSubmit"
OnInvalidSubmit="Full_OnInvalidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<InputText class="form-control" @bind-Value="FullModel.Username" />
<ValidationMessage For="@(() => FullModel.Username)" />
<InputNumber class="form-control" @bind-Value="FullModel.Age" />
<ValidationMessage For="@(() => FullModel.Age)" />
<InputSelect class="form-select" @bind-Value="FullModel.Role">...</InputSelect>
<ValidationMessage For="@(() => FullModel.Role)" />
<InputDate class="form-control" @bind-Value="FullModel.Birthday" />
<ValidationMessage For="@(() => FullModel.Birthday)" />
<InputText type="password" class="form-control" @bind-Value="FullModel.Password" />
<ValidationMessage For="@(() => FullModel.Password)" />
<InputText type="password" class="form-control" @bind-Value="FullModel.ConfirmPassword" />
<ValidationMessage For="@(() => FullModel.ConfirmPassword)" />
<InputCheckbox class="form-check-input" @bind-Value="FullModel.Accepted" />
<ValidationMessage For="@(() => FullModel.Accepted)" />
<button type="submit">Submit</button>
</EditForm>
@code {
private FullUser FullModel = new();
private string FullStatus = "none";
private void Full_OnValidSubmit() => FullStatus = "✅ Valid submit";
private void Full_OnInvalidSubmit() => FullStatus = "❌ Invalid submit";
private void Full_Reset()
{
FullModel = new();
FullStatus = "reset";
}
public class FullUser
{
[Required, StringLength(20, MinimumLength = 3)]
public string Username { get; set; } = "";
[Range(18, 60)]
public int Age { get; set; } = 18;
[Required]
public string Role { get; set; } = "";
[Required]
public DateTime? Birthday { get; set; } = DateTime.Today;
[Required, MinLength(6)]
public string Password { get; set; } = "";
[Compare(nameof(Password), ErrorMessage = "Passwords do not match")]
public string ConfirmPassword { get; set; } = "";
[Range(typeof(bool), "true", "true", ErrorMessage = "You must accept terms")]
public bool Accepted { get; set; }
}
}
3️⃣ EditContext + FieldChanged – theo dõi field “dirty”
Xem code
<EditForm EditContext="@TrackContext" OnSubmit="Track_OnSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<InputText class="form-control" @bind-Value="TrackModel.Title" />
<ValidationMessage For="@(() => TrackModel.Title)" />
<InputNumber class="form-control" @bind-Value="TrackModel.Quantity" />
<ValidationMessage For="@(() => TrackModel.Quantity)" />
<button type="submit">Submit</button>
</EditForm>
@code {
private TrackItem TrackModel = new();
private EditContext TrackContext = default!;
private HashSet<string> DirtyFields = new();
private string TrackStatus = "none";
protected override void OnInitialized()
{
TrackContext = new EditContext(TrackModel);
TrackContext.OnFieldChanged += Track_OnFieldChanged;
}
private void Track_OnFieldChanged(object? sender, FieldChangedEventArgs e)
=> DirtyFields.Add(e.FieldIdentifier.FieldName);
private void Track_OnSubmit()
{
var ok = TrackContext.Validate(); // validate thủ công
TrackStatus = ok ? "✅ Valid" : "❌ Invalid";
}
private void Track_Reset()
{
DirtyFields.Clear();
TrackStatus = "reset";
TrackContext.OnFieldChanged -= Track_OnFieldChanged;
TrackModel = new TrackItem();
TrackContext = new EditContext(TrackModel);
TrackContext.OnFieldChanged += Track_OnFieldChanged;
}
public class TrackItem
{
[Required]
public string Title { get; set; } = "";
[Range(1, 999)]
public int Quantity { get; set; } = 1;
}
}
4️⃣ Custom validation – Username đã tồn tại (ValidationMessageStore)
Xem code
<EditForm EditContext="@CustomContext" OnSubmit="Custom_OnSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<InputText class="form-control" @bind-Value="CustomModel.Username" />
<ValidationMessage For="@(() => CustomModel.Username)" />
<button type="submit">Submit</button>
</EditForm>
@code {
private CustomUser CustomModel = new();
private EditContext CustomContext = default!;
private ValidationMessageStore CustomStore = default!;
private string CustomStatus = "none";
protected override void OnInitialized()
{
CustomContext = new EditContext(CustomModel);
CustomStore = new ValidationMessageStore(CustomContext);
}
private void Custom_OnSubmit()
{
// 1) Clear lỗi custom cũ
CustomStore.Clear();
// 2) Validate theo DataAnnotations
var ok = CustomContext.Validate();
// 3) Rule custom: username bị trùng
var taken = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{ "admin", "root" };
if (!string.IsNullOrWhiteSpace(CustomModel.Username) && taken.Contains(CustomModel.Username))
{
ok = false;
CustomStore.Add(
CustomContext.Field(nameof(CustomModel.Username)),
"Username already exists."
);
// báo Blazor cập nhật validation UI
CustomContext.NotifyValidationStateChanged();
}
CustomStatus = ok ? "✅ Valid" : "❌ Invalid";
}
private void Custom_Reset()
{
CustomModel = new CustomUser();
CustomStatus = "reset";
CustomContext = new EditContext(CustomModel);
CustomStore = new ValidationMessageStore(CustomContext);
}
public class CustomUser
{
[Required, StringLength(20, MinimumLength = 3)]
public string Username { get; set; } = "";
}
}