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

Status: none

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”

Dirty fields:none

Status: none

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)

Status: none

Demo: nhập admin hoặc root sẽ báo “đã tồn tại”.

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; } = "";
    }
}

An error has occurred. This application may no longer respond until reloaded. Reload 🗙
Web hosting by Somee.com