{"id":31282245,"url":"https://github.com/loresoft/blazilla","last_synced_at":"2025-09-24T03:45:59.603Z","repository":{"id":314554920,"uuid":"1055906696","full_name":"loresoft/Blazilla","owner":"loresoft","description":"A library for using FluentValidation with Blazor EditForm components. Provides seamless integration between FluentValidation and Blazor forms with support for real-time validation, nested objects, async validation, and rule sets.","archived":false,"fork":false,"pushed_at":"2025-09-14T03:45:06.000Z","size":147,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-14T04:22:15.908Z","etag":null,"topics":["asp-net-core","aspnetcore","blazor","component","fluentvalidation","validation"],"latest_commit_sha":null,"homepage":"https://loresoft.com/Blazilla/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/loresoft.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"loresoft"}},"created_at":"2025-09-13T02:43:47.000Z","updated_at":"2025-09-14T03:40:31.000Z","dependencies_parsed_at":"2025-09-14T04:22:18.542Z","dependency_job_id":null,"html_url":"https://github.com/loresoft/Blazilla","commit_stats":null,"previous_names":["loresoft/loresoft.blazor.fluentvalidation","loresoft/blazilla"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/loresoft/Blazilla","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loresoft%2FBlazilla","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loresoft%2FBlazilla/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loresoft%2FBlazilla/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loresoft%2FBlazilla/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/loresoft","download_url":"https://codeload.github.com/loresoft/Blazilla/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loresoft%2FBlazilla/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":276689016,"owners_count":25686611,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-24T02:00:09.776Z","response_time":97,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asp-net-core","aspnetcore","blazor","component","fluentvalidation","validation"],"created_at":"2025-09-24T03:45:54.335Z","updated_at":"2025-09-24T03:45:59.589Z","avatar_url":"https://github.com/loresoft.png","language":"C#","readme":"# Blazilla\n\nA library for using FluentValidation with Blazor\n\n[![Nuget version](https://img.shields.io/nuget/v/Blazilla.svg?logo=nuget)](https://www.nuget.org/packages/Blazilla/)\n[![Build Status](https://github.com/loresoft/Blazilla/actions/workflows/dotnet.yml/badge.svg)](https://github.com/loresoft/Blazilla/actions)\n[![Coverage Status](https://coveralls.io/repos/github/loresoft/Blazilla/badge.svg?branch=main)](https://coveralls.io/github/loresoft/Blazilla?branch=main)\n[![License](https://img.shields.io/github/license/loresoft/Blazilla.svg)](LICENSE)\n\n## Overview\n\nBlazilla provides seamless integration between [FluentValidation](https://fluentvalidation.net/) and Blazor's `EditForm` component. This library enables you to use FluentValidation's powerful and flexible validation rules with Blazor forms, supporting both Blazor Server and Blazor WebAssembly applications.\n\n## Features\n\n- **Real-time validation** - Validate fields as users type or change values\n- **Form-level validation** - Full model validation on form submission\n- **Nested object validation** - Support for complex object hierarchies\n- **Asynchronous validation** - Built-in support for async validation rules\n- **Rule sets** - Execute specific groups of validation rules\n- **Custom validator selectors** - Fine-grained control over which rules to execute\n- **Dependency injection integration** - Automatic validator resolution from DI container\n- **Performance optimized** - Compiled expression trees for fast validation context creation\n\n## Installation\n\nInstall the package via NuGet:\n\n```bash\ndotnet add package Blazilla\n```\n\nOr via Package Manager Console:\n\n```bash\nInstall-Package Blazilla\n```\n\n## Quick Start\n\n### 1. Create a Model\n\n```csharp\npublic class Person\n{\n    public string? FirstName { get; set; }\n    public string? LastName { get; set; }\n    public int Age { get; set; }\n    public string? EmailAddress { get; set; }\n}\n```\n\n### 2. Create a FluentValidation Validator\n\n```csharp\nusing FluentValidation;\n\npublic class PersonValidator : AbstractValidator\u003cPerson\u003e\n{\n    public PersonValidator()\n    {\n        RuleFor(p =\u003e p.FirstName)\n            .NotEmpty().WithMessage(\"First name is required\")\n            .MaximumLength(50).WithMessage(\"First name cannot exceed 50 characters\");\n\n        RuleFor(p =\u003e p.LastName)\n            .NotEmpty().WithMessage(\"Last name is required\")\n            .MaximumLength(50).WithMessage(\"Last name cannot exceed 50 characters\");\n\n        RuleFor(p =\u003e p.Age)\n            .GreaterThanOrEqualTo(0).WithMessage(\"Age must be greater than or equal to 0\")\n            .LessThan(150).WithMessage(\"Age must be less than 150\");\n\n        RuleFor(p =\u003e p.EmailAddress)\n            .NotEmpty().WithMessage(\"Email address is required\")\n            .EmailAddress().WithMessage(\"Please provide a valid email address\");\n    }\n}\n```\n\n### 3. Register the Validator\n\n#### Blazor Server (`Program.cs`)\n\n```csharp\nusing FluentValidation;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n// Add services\nbuilder.Services.AddRazorPages();\nbuilder.Services.AddServerSideBlazor();\n\n// Register FluentValidation validators as singletons for better performance\nbuilder.Services.AddSingleton\u003cIValidator\u003cPerson\u003e, PersonValidator\u003e();\n\nvar app = builder.Build();\n// ... rest of configuration\n```\n\n#### Blazor WebAssembly (`Program.cs`)\n\n```csharp\nusing FluentValidation;\nusing Microsoft.AspNetCore.Components.Web;\nusing Microsoft.AspNetCore.Components.WebAssembly.Hosting;\n\nvar builder = WebAssemblyHostBuilder.CreateDefault(args);\nbuilder.RootComponents.Add\u003cApp\u003e(\"#app\");\nbuilder.RootComponents.Add\u003cHeadOutlet\u003e(\"head::after\");\n\n// Register FluentValidation validators as singletons for better performance\nbuilder.Services.AddSingleton\u003cIValidator\u003cPerson\u003e, PersonValidator\u003e();\n\nawait builder.Build().RunAsync();\n```\n\n### 4. Use in Blazor Component\n\n```razor\n@page \"/person-form\"\n\n\u003ch3\u003ePerson Form\u003c/h3\u003e\n\n\u003cEditForm Model=\"@person\" OnValidSubmit=\"@HandleValidSubmit\"\u003e\n    \u003cFluentValidator /\u003e\n    \u003cValidationSummary /\u003e\n\n    \u003cdiv class=\"mb-3\"\u003e\n        \u003clabel for=\"firstName\" class=\"form-label\"\u003eFirst Name\u003c/label\u003e\n        \u003cInputText id=\"firstName\" class=\"form-control\" @bind-Value=\"person.FirstName\" /\u003e\n        \u003cValidationMessage For=\"@(() =\u003e person.FirstName)\" /\u003e\n    \u003c/div\u003e\n\n    \u003cdiv class=\"mb-3\"\u003e\n        \u003clabel for=\"lastName\" class=\"form-label\"\u003eLast Name\u003c/label\u003e\n        \u003cInputText id=\"lastName\" class=\"form-control\" @bind-Value=\"person.LastName\" /\u003e\n        \u003cValidationMessage For=\"@(() =\u003e person.LastName)\" /\u003e\n    \u003c/div\u003e\n\n    \u003cdiv class=\"mb-3\"\u003e\n        \u003clabel for=\"age\" class=\"form-label\"\u003eAge\u003c/label\u003e\n        \u003cInputNumber id=\"age\" class=\"form-control\" @bind-Value=\"person.Age\" /\u003e\n        \u003cValidationMessage For=\"@(() =\u003e person.Age)\" /\u003e\n    \u003c/div\u003e\n\n    \u003cdiv class=\"mb-3\"\u003e\n        \u003clabel for=\"email\" class=\"form-label\"\u003eEmail\u003c/label\u003e\n        \u003cInputText id=\"email\" class=\"form-control\" @bind-Value=\"person.EmailAddress\" /\u003e\n        \u003cValidationMessage For=\"@(() =\u003e person.EmailAddress)\" /\u003e\n    \u003c/div\u003e\n\n    \u003cbutton type=\"submit\" class=\"btn btn-primary\"\u003eSubmit\u003c/button\u003e\n\u003c/EditForm\u003e\n\n@code {\n    private Person person = new();\n\n    private async Task HandleValidSubmit()\n    {\n        // Handle successful form submission\n        Console.WriteLine(\"Form submitted successfully!\");\n    }\n}\n```\n\n## Advanced Usage\n\n### Asynchronous Validation\n\nBlazor's built-in validation system doesn't natively support asynchronous validation. When using async validation rules with FluentValidation, you need to handle form submission manually to ensure async validation completes before the form is submitted.\n\nEnable asynchronous validation mode and use `OnSubmit` instead of `OnValidSubmit` to properly handle async validation:\n\n```razor\n\u003cEditForm Model=\"@person\" OnSubmit=\"@HandleSubmit\"\u003e\n    \u003cFluentValidator AsyncMode=\"true\" /\u003e\n    \u003cValidationSummary /\u003e\n    \n    \u003cdiv class=\"mb-3\"\u003e\n        \u003clabel for=\"email\" class=\"form-label\"\u003eEmail\u003c/label\u003e\n        \u003cInputText id=\"email\" class=\"form-control\" @bind-Value=\"person.EmailAddress\" /\u003e\n        \u003cValidationMessage For=\"@(() =\u003e person.EmailAddress)\" /\u003e\n    \u003c/div\u003e\n    \n    \u003cbutton type=\"submit\" class=\"btn btn-primary\" disabled=\"@isSubmitting\"\u003e\n        @(isSubmitting ? \"Validating...\" : \"Submit\")\n    \u003c/button\u003e\n\u003c/EditForm\u003e\n\n@code {\n    private Person person = new();\n    private bool isSubmitting = false;\n\n    private async Task HandleSubmit(EditContext editContext)\n    {\n        isSubmitting = true;\n        StateHasChanged();\n        \n        try\n        {\n            // Use ValidateAsync to ensure all async validation completes\n            var isValid = await editContext.ValidateAsync();\n            \n            if (isValid)\n            {\n                // Form is valid, proceed with submission\n                await ProcessValidForm();\n            }\n            // If invalid, validation messages will be displayed automatically\n        }\n        finally\n        {\n            isSubmitting = false;\n            StateHasChanged();\n        }\n    }\n    \n    private async Task ProcessValidForm()\n    {\n        // Handle successful form submission\n        Console.WriteLine(\"Form submitted successfully!\");\n        await Task.Delay(1000); // Simulate form processing\n    }\n}\n```\n\nCreate a validator with async rules:\n\n```csharp\npublic class PersonValidator : AbstractValidator\u003cPerson\u003e\n{\n    public PersonValidator()\n    {\n        RuleFor(p =\u003e p.EmailAddress)\n            .NotEmpty().WithMessage(\"Email is required\")\n            .EmailAddress().WithMessage(\"Please provide a valid email address\")\n            .MustAsync(async (email, cancellation) =\u003e \n            {\n                // Simulate async validation (e.g., database check)\n                await Task.Delay(500, cancellation);\n                return !email?.Equals(\"admin@example.com\", StringComparison.OrdinalIgnoreCase) ?? true;\n            }).WithMessage(\"This email address is not available\");\n    }\n}\n```\n\n#### Why This Approach is Necessary\n\nBlazor's `OnValidSubmit` event fires immediately after synchronous validation passes, without waiting for async validation to complete. This can result in forms being submitted with incomplete validation results. \n\nThe `EditContextExtensions.ValidateAsync()` method:\n\n1. Triggers synchronous validation first (which may initiate async validation tasks)\n2. Waits for any pending async validation tasks to complete\n3. Returns `true` only when all validation (sync and async) has passed\n\nThis ensures that form submission is properly prevented when async validation rules fail.\n\n\u003e **Important**: Always use `OnSubmit` instead of `OnValidSubmit` when working with async validation rules. The `OnValidSubmit` event doesn't wait for async validation to complete.\n\n### Rule Sets\n\nUse rule sets to execute specific groups of validation rules:\n\n```csharp\npublic class PersonValidator : AbstractValidator\u003cPerson\u003e\n{\n    public PersonValidator()\n    {\n        // Default rules (always executed)\n        RuleFor(p =\u003e p.FirstName).NotEmpty();\n\n        // Rules in specific rule sets\n        RuleSet(\"Create\", () =\u003e\n        {\n            RuleFor(p =\u003e p.EmailAddress)\n                .NotEmpty()\n                .EmailAddress();\n        });\n\n        RuleSet(\"Update\", () =\u003e\n        {\n            RuleFor(p =\u003e p.LastName).NotEmpty();\n        });\n    }\n}\n```\n\n```razor\n\u003c!-- Execute only the \"Create\" rule set --\u003e\n\u003cFluentValidator RuleSets=\"@(new[] { \"Create\" })\" /\u003e\n\n\u003c!-- Execute all rules including those in rule sets --\u003e\n\u003cFluentValidator AllRules=\"true\" /\u003e\n```\n\n### Nested Object Validation\n\nValidate complex objects with nested properties:\n\n```csharp\npublic class Person\n{\n    public string? FirstName { get; set; }\n    public string? LastName { get; set; }\n    public Address? Address { get; set; }\n}\n\npublic class Address\n{\n    public string? Street { get; set; }\n    public string? City { get; set; }\n    public string? PostalCode { get; set; }\n}\n\npublic class AddressValidator : AbstractValidator\u003cAddress\u003e\n{\n    public AddressValidator()\n    {\n        RuleFor(a =\u003e a.Street).NotEmpty().WithMessage(\"Street is required\");\n        RuleFor(a =\u003e a.City).NotEmpty().WithMessage(\"City is required\");\n        RuleFor(a =\u003e a.PostalCode).NotEmpty().WithMessage(\"Postal code is required\");\n    }\n}\n\npublic class PersonValidator : AbstractValidator\u003cPerson\u003e\n{\n    public PersonValidator()\n    {\n        RuleFor(p =\u003e p.FirstName).NotEmpty();\n        RuleFor(p =\u003e p.LastName).NotEmpty();\n        \n        // Validate nested Address object\n        RuleFor(p =\u003e p.Address!)\n            .SetValidator(new AddressValidator())\n            .When(p =\u003e p.Address is not null);\n    }\n}\n```\n\n```razor\n\u003cEditForm Model=\"@person\" OnValidSubmit=\"@HandleValidSubmit\"\u003e\n    \u003cFluentValidator /\u003e\n    \n    \u003c!-- Person fields --\u003e\n    \u003cInputText @bind-Value=\"person.FirstName\" /\u003e\n    \u003cValidationMessage For=\"@(() =\u003e person.FirstName)\" /\u003e\n    \n    \u003c!-- Nested address fields --\u003e\n    \u003cInputText @bind-Value=\"person.Address!.Street\" /\u003e\n    \u003cValidationMessage For=\"@(() =\u003e person.Address!.Street)\" /\u003e\n    \n    \u003cInputText @bind-Value=\"person.Address!.City\" /\u003e\n    \u003cValidationMessage For=\"@(() =\u003e person.Address!.City)\" /\u003e\n\u003c/EditForm\u003e\n```\n\n### Custom Validator Instance\n\nPass a validator instance directly instead of using dependency injection:\n\n```razor\n@code {\n    private PersonValidator validator = new();\n}\n\n\u003cEditForm Model=\"@person\" OnValidSubmit=\"@HandleValidSubmit\"\u003e\n    \u003cFluentValidator Validator=\"@validator\" /\u003e\n    \u003c!-- form fields --\u003e\n\u003c/EditForm\u003e\n```\n\n### Custom Validator Selector\n\nImplement fine-grained control over which validation rules to execute:\n\n```razor\n\u003cEditForm Model=\"@person\" OnValidSubmit=\"@HandleValidSubmit\"\u003e\n    \u003cFluentValidator Selector=\"@customSelector\" /\u003e\n    \u003c!-- form fields --\u003e\n\u003c/EditForm\u003e\n\n@code {\n    private IValidatorSelector customSelector = new CustomValidatorSelector();\n    \n    public class CustomValidatorSelector : IValidatorSelector\n    {\n        public bool CanExecute(IValidationRule rule, string propertyPath, IValidationContext context)\n        {\n            // Custom logic to determine if a rule should execute\n            return true;\n        }\n    }\n}\n```\n\n## Component Parameters\n\nThe `FluentValidator` component supports the following parameters:\n\n| Parameter   | Type                   | Default | Description                                                                                                                                                                                                                         |\n| ----------- | ---------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `Validator` | `IValidator?`          | `null`  | The FluentValidation validator instance to use. If not provided, the component will attempt to resolve a validator from the dependency injection container based on the EditForm's model type.                                      |\n| `RuleSets`  | `IEnumerable\u003cstring\u003e?` | `null`  | Collection of rule set names to execute. Only validation rules within the specified rule sets will be executed. Use this to run targeted validation scenarios (e.g., \"Create\", \"Update\", \"Profile\").                                |\n| `AllRules`  | `bool`                 | `false` | When true, executes all validation rules including those defined within rule sets. When false (default), only executes rules not in any rule set plus any specified in the RuleSets parameter.                                      |\n| `AsyncMode` | `bool`                 | `false` | Enables asynchronous validation mode for validators containing async rules (MustAsync, etc.). When enabled, validation will be performed asynchronously and may show a brief delay for async operations like database checks.       |\n| `Selector`  | `IValidatorSelector?`  | `null`  | Custom validator selector implementation for advanced scenarios requiring fine-grained control over which validation rules execute. Allows conditional rule execution based on custom logic, property paths, or validation context. |\n\n## Validation Behavior\n\n- **Field-level validation**: Triggered when individual form fields change\n- **Form-level validation**: Triggered when the form is submitted via `OnValidSubmit`\n- **Real-time feedback**: Validation messages appear immediately as users interact with the form\n- **Integration with EditContext**: Works seamlessly with Blazor's built-in validation system\n\n## Performance Considerations\n\n- Validators should be registered as singletons in the DI container since they are stateless and can be safely shared across requests/components\n- Validators are cached and reused when resolved from dependency injection\n- Validation contexts are created using compiled expression trees for optimal performance\n- Asynchronous validation executes async validators directly, ensuring responsive UI performance\n\n\u003e **AsyncMode**: Only enable `AsyncMode=\"true\"` when your validators contain actual async rules (like `MustAsync`). Using async mode with purely synchronous validators introduces unnecessary overhead from async state machine generation and task scheduling, even though the validation logic itself is synchronous\n\n## Integration Details\n\n### Blazor FieldIdentifier to FluentValidation Path Conversion\n\nThe library automatically converts Blazor's `FieldIdentifier` objects to FluentValidation property paths to ensure validation messages appear in the correct location. This conversion handles:\n\n**Simple Properties**\n```csharp\n// Blazor FieldIdentifier: { Model = person, FieldName = \"FirstName\" }\n// FluentValidation path: \"FirstName\"\n```\n\n**Nested Properties**\n```csharp\n// Blazor FieldIdentifier: { Model = address, FieldName = \"Street\" }\n// FluentValidation path: \"Address.Street\" (when address is a property of person)\n```\n\n**Collection Items**\n```csharp\n// Blazor FieldIdentifier: { Model = phoneNumber, FieldName = \"Number\" }\n// FluentValidation path: \"PhoneNumbers[0].Number\" (when phoneNumber is item 0 in a collection)\n```\n\nThe path conversion is performed internally using object tree analysis to match Blazor's field identification system with FluentValidation's property path conventions. This ensures that validation messages from FluentValidation rules are correctly associated with the corresponding Blazor input components.\n\n### Blazor Forms and Async Validation Limitations\n\n**Important**: Blazor's built-in form validation system has inherent limitations with asynchronous validation:\n\n1. **OnValidSubmit Event Timing**: The `OnValidSubmit` event fires immediately after synchronous validation passes, without waiting for any asynchronous validation operations to complete.\n\n2. **EditContext.Validate() Behavior**: The standard `EditContext.Validate()` method only performs synchronous validation and doesn't trigger or wait for async validation rules.\n\n**Workaround for Async Validation**:\n\nWhen using validators with async rules (`MustAsync`, custom async validators), you must:\n\n- Enable `AsyncMode=\"true\"` on the `FluentValidator` component\n- Use `OnSubmit` instead of `OnValidSubmit`\n- Call `EditContextExtensions.ValidateAsync()` to ensure async validation completes\n\nThis limitation is a fundamental aspect of Blazor's validation architecture and affects all validation libraries, not just FluentValidation integrations.\n\n## Troubleshooting\n\n### Common Issues\n\n**No validator found error**\n\nNo validator found for model type MyModel. To use FluentValidator, register a validator for this model type or pass one directly to the Validator parameter.\n\n**Solution**: Ensure your validator is registered in the DI container:\n\n```csharp\nbuilder.Services.AddSingleton\u003cIValidator\u003cMyModel\u003e, MyModelValidator\u003e();\n```\n\n**EditContext parameter missing**\n\nFluentValidator requires a cascading parameter of type EditContext.\n\n**Solution**: Ensure the component is placed inside an `EditForm`:\n\n```razor\n\u003cEditForm Model=\"@model\"\u003e\n    \u003cFluentValidator /\u003e\n    \u003c!-- form content --\u003e\n\u003c/EditForm\u003e\n```\n\n**Invalid form submits when using AsyncMode**\n\nWhen using `AsyncMode=\"true\"`, forms may submit even when async validation fails if you're using `OnValidSubmit` instead of `OnSubmit`.\n\n**Problem**: `OnValidSubmit` fires immediately after synchronous validation passes, without waiting for async validation to complete.\n\n**Solution**: Use `OnSubmit` with `EditContextExtensions.ValidateAsync()`:\n\n```razor\n\u003c!-- ❌ Incorrect - Don't use OnValidSubmit with async validation --\u003e\n\u003cEditForm Model=\"@model\" OnValidSubmit=\"@HandleValidSubmit\"\u003e\n    \u003cFluentValidator AsyncMode=\"true\" /\u003e\n    \u003c!-- form fields --\u003e\n\u003c/EditForm\u003e\n\n\u003c!-- ✅ Correct - Use OnSubmit with ValidateAsync --\u003e\n\u003cEditForm Model=\"@model\" OnSubmit=\"@HandleSubmit\"\u003e\n    \u003cFluentValidator AsyncMode=\"true\" /\u003e\n    \u003c!-- form fields --\u003e\n\u003c/EditForm\u003e\n\n@code {\n    private async Task HandleSubmit(EditContext editContext)\n    {\n        // Wait for all async validation to complete\n        var isValid = await editContext.ValidateAsync();\n        \n        if (isValid)\n        {\n            // Only proceed if validation passed\n            await ProcessForm();\n        }\n    }\n}\n```\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","funding_links":["https://github.com/sponsors/loresoft"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Floresoft%2Fblazilla","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Floresoft%2Fblazilla","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Floresoft%2Fblazilla/lists"}