Title here
Summary here
Rizzy aims to provide a familiar validation experience, similar to traditional ASP.NET Core MVC or Razor Pages, by leveraging Data Annotations and integrating seamlessly with Blazor’s EditContext
. This allows for both client-side feedback (when paired with a suitable JS library) and robust server-side validation.
EditForm
: The container for your form inputs. Rizzy often uses this in conjunction with HTMX attributes for submissions (e.g., hx-post
).DataAnnotationsValidator
: Attaches validation support based on Data Annotation attributes ([Required]
, [StringLength]
, etc.) on your model to the EditForm
’s EditContext
.RzInput*
Components: (e.g., RzInputText
, RzInputNumber
) These components automatically generate standard HTML input elements along with data-val-*
attributes based on the Data Annotations found on the bound model property. These attributes are essential for client-side validation libraries.RzValidationMessage
: Displays validation messages for a specific field, similar to asp-validation-for
.RzValidationSummary
: Displays a summary of all validation messages for the form, similar to asp-validation-summary
.RzInitialValidator
: A crucial component within the EditForm
. On initial render or after a postback (like an HTMX form submission), it checks if the ModelState
(cascaded from the RzController
) contains errors.ModelState
has errors, it adds them to the EditContext
.ModelState
is valid, it triggers the standard EditContext
validation (based on Data Annotations) to ensure the form reflects the current model’s validity state immediately.Let’s illustrate with a model containing various validation rules and the corresponding Razor component form.
SignupViewModel.cs
)using System.ComponentModel.DataAnnotations;
public class SignupViewModel
{
[Required(ErrorMessage = "Username is required.")]
[StringLength(20, MinimumLength = 5, ErrorMessage = "Username must be between 5 and 20 characters.")]
[RegularExpression("^[a-zA-Z0-9_]*$", ErrorMessage = "Username can only contain letters, numbers, and underscores.")]
public string? Username { get; set; }
[Required(ErrorMessage = "Email is required.")]
[EmailAddress(ErrorMessage = "Please enter a valid email address.")]
public string? Email { get; set; }
[Required(ErrorMessage = "Password is required.")]
[DataType(DataType.Password)]
[StringLength(100, MinimumLength = 8, ErrorMessage = "Password must be at least 8 characters long.")]
public string? Password { get; set; }
[Required(ErrorMessage = "Please confirm your password.")]
[DataType(DataType.Password)]
[Compare(nameof(Password), ErrorMessage = "Passwords do not match.")]
public string? ConfirmPassword { get; set; }
[Range(18, 120, ErrorMessage = "You must be between 18 and 120 years old.")]
public int? Age { get; set; } // Optional field with Range validation
[Required(ErrorMessage = "You must agree to the terms.")]
[Range(typeof(bool), "true", "true", ErrorMessage = "You must accept the terms and conditions.")]
public bool AcceptTerms { get; set; }
// Example of a custom validation attribute (implementation not shown here)
// [MustBeAwesome(ErrorMessage = "Your profile description needs more awesome!")]
// public string ProfileDescription { get; set; }
}
SignupForm.razor
)@using System.ComponentModel.DataAnnotations
@using Rizzy; // Assuming Rizzy components are imported
<div id="signup-form-container">
<h3>Sign Up</h3>
<EditForm Model="Model" FormName="Signup" OnValidSubmit="HandleValidSubmit" hx-post="/signup/submit" hx-target="#signup-form-container" hx-swap="outerHTML">
<DataAnnotationsValidator />
<RzInitialValidator />
<RzValidationSummary class="alert alert-danger" />
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<RzInputText id="username" @bind-Value="Model.Username" class="form-control" />
<RzValidationMessage For="@(() => Model.Username)" class="text-danger" />
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<RzInputText id="email" @bind-Value="Model.Email" class="form-control" type="email" />
<RzValidationMessage For="@(() => Model.Email)" class="text-danger" />
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<RzInputText id="password" @bind-Value="Model.Password" class="form-control" type="password" />
<RzValidationMessage For="@(() => Model.Password)" class="text-danger" />
</div>
<div class="mb-3">
<label for="confirmPassword" class="form-label">Confirm Password</label>
<RzInputText id="confirmPassword" @bind-Value="Model.ConfirmPassword" class="form-control" type="password" />
<RzValidationMessage For="@(() => Model.ConfirmPassword)" class="text-danger" />
</div>
<div class="mb-3">
<label for="age" class="form-label">Age (Optional)</label>
<RzInputNumber id="age" @bind-Value="Model.Age" class="form-control" />
<RzValidationMessage For="@(() => Model.Age)" class="text-danger" />
</div>
<div class="mb-3 form-check">
<RzInputCheckbox id="acceptTerms" @bind-Value="Model.AcceptTerms" class="form-check-input" />
<label for="acceptTerms" class="form-check-label">I accept the Terms and Conditions</label>
<RzValidationMessage For="@(() => Model.AcceptTerms)" class="text-danger d-block" />
@* Use d-block for checkbox message alignment if needed *@
</div>
<button type="submit" class="btn btn-primary">Sign Up</button>
</EditForm>
</div>
@code {
// Model can be supplied via [Parameter] or [SupplyParameterFromForm]
[SupplyParameterFromForm]
private SignupViewModel Model { get; set; } = new();
// Example handler for Blazor-based valid submission (optional if using pure HTMX post)
private void HandleValidSubmit()
{
Console.WriteLine("Form submitted successfully within Blazor context (if OnValidSubmit is used).");
// This might not be hit if only hx-post is used for submission
}
protected override void OnInitialized()
{
// Potential initialization logic
base.OnInitialized();
}
}
[Required]
, [StringLength]
, [Compare]
, etc.).RzInput*
components read these annotations via the EditContext
(populated by DataAnnotationsValidator
).data-val-*
Attributes: Rizzy generates the standard data-val="true"
, data-val-required="Error message"
, data-val-length-min="5"
, etc., attributes on the HTML input elements.aspnet-validation.js
included with RizzyUI, or others) is required to interpret these data-val-*
attributes. This library hooks into form events (like input changes or blur) and displays/hides validation messages (<span>
elements typically generated alongside inputs or targeted by RzValidationMessage
) without a server roundtrip.RzValidationMessage
/ RzValidationSummary
: These components render the necessary HTML elements (usually <span>
or <div>
) that client-side libraries use to display the error messages defined in your annotations or added dynamically.hx-post
(or similar).ModelState
.ModelState
Check: Your controller action checks ModelState.IsValid
.ModelState
:PartialView<YourFormComponent>()
(or View
).ModelState
dictionary down to the component.EditForm
, RzInitialValidator
detects the ModelState
errors and adds them to the EditContext
.RzValidationMessage
and RzValidationSummary
render the errors from the EditContext
.ModelState
: The controller processes the data and typically returns a different response (e.g., a redirect, a success message partial, etc.).This combination ensures immediate feedback via client-side validation (if configured with a JS library) and guarantees data integrity through server-side validation, leveraging Rizzy components to bridge the gap.