Skip to main content 홈 크리에이터 managedcode dotnet-skills collect-user-input
collect-user-input Build forms, validate data, and react to user input in Blazor. USE FOR adding forms, search boxes, filter panels, inline editing, data-entry UI, file uploads, validation (annotations or custom), handling form submissions, and binding input controls. Covers EditForm, built-in input components, DataAnnotationsValidator, custom validation, SSR form patterns (SupplyParameterFromForm, FormName, AntiforgeryToken, Enhance), and @bind for simple interactive controls. DO NOT USE for project scaffolding (see create-blazor-project) or prerendering issues (see support-prerendering).
설치로 이동 Skills Marketplace 커뮤니티가 만든 AI 스킬을 발견하고 탐색하세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/managedcode/dotnet-skills --skill collect-user-input명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Zip 다운로드 다운로드 중... Build, upgrade, and operate Aspire 13.5.x C# or TypeScript application hosts with the current CLI, AppHost, ServiceDefaults, integrations, dashboard, testing, MCP, and deployment patterns for distributed apps. USE FOR: Aspire.AppHost.Sdk, Aspire.Hosting.*, DistributedApplication.CreateBuilder, apphost.mts, createBuilder, WithReference, WaitFor, AddProject, AddRedis, AddPostgres, aspire run, aspire init, aspire. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.
Design Microsoft Orleans systems from each primitive's purpose and failure model. USE FOR: grains, digital twins, state versus databases, transactions, messaging, streams, timers, reminders, Durable Jobs, stateless workers, grain services, startup, and hosting. DO NOT USE FOR: other actor stacks, batches, relational-only CRUD, or advice without an Orleans decision. INVOKES: inspect version and topology, choose the primitive, implement, and validate.
license MIT name collect-user-input description Build forms, validate data, and react to user input in Blazor. USE FOR adding forms, search boxes, filter panels, inline editing, data-entry UI, file uploads, validation (annotations or custom), handling form submissions, and binding input controls. Covers EditForm, built-in input components, DataAnnotationsValidator, custom validation, SSR form patterns (SupplyParameterFromForm, FormName, AntiforgeryToken, Enhance), and @bind for simple interactive controls. DO NOT USE for project scaffolding (see create-blazor-project) or prerendering issues (see support-prerendering).
Collect User Input
Step 1 — Read the Project's AGENTS.md
Check AGENTS.md for Interactivity Mode and Interactivity Scope . This determines which form patterns apply:
Mode Form mechanism None (Static SSR) EditForm with FormName + [SupplyParameterFromForm]. No @bind, no @onchange.Server EditForm with @bind-Value. Full interactivity — real-time validation, dynamic UI.WebAssembly Same as Server, but validators needing server data must call APIs. Auto Same as WebAssembly — code must work in both browser and server.
Global All forms are interactive. FormName only needed when explicitly opting a page to static SSR. Per-page Forms in static pages use FormName + [SupplyParameterFromForm]. Forms in @rendermode pages use @bind-Value.
EditForm Setup EditForm requires either Model or EditContext — never both.
Model-based (default) <EditForm Model="Employee" OnValidSubmit="HandleSubmit" FormName="employee">
<DataAnnotationsValidator />
<ValidationSummary />
<label>
Name: <InputText @bind-Value="Employee!.Name" />
<ValidationMessage For="() => Employee!.Name" />
</label>
<button type="submit">Save</button>
</EditForm>
@code {
[SupplyParameterFromForm]
private EmployeeModel? Employee { get; set; }
protected override void OnInitialized() => Employee ??= new();
private async Task HandleSubmit()
{
// Save Employee
}
}
This single pattern works in both SSR and interactive modes:
In SSR: FormName identifies the form, [SupplyParameterFromForm] binds POST data, ??= initializes on GET.
In interactive: @bind-Value provides two-way binding, [SupplyParameterFromForm] is ignored, FormName is harmless.
EditContext-based (advanced) Use when you need programmatic field tracking, dynamic validation rules, or manual EditContext.Validate() calls:
private EditContext? editContext;
private EmployeeModel model = new ();
protected override void OnInitialized ()
{
editContext = new EditContext(model);
}
<EditForm EditContext="editContext" OnValidSubmit="HandleSubmit" FormName="employee">
Submit Handlers Handler Fires when Use when OnValidSubmitValidation passes Standard forms with DataAnnotationsValidator OnInvalidSubmitValidation fails Need custom handling for invalid state OnSubmitAlways — validation is manual Using EditContext.Validate() yourself
OnSubmit cannot combine with OnValidSubmit/OnInvalidSubmit.
Built-in Input Components Component Binds to Notes InputTextstringRenders <input type="text"> InputTextAreastringRenders <textarea> InputNumber<T>int, double, decimalRenders <input type="number"> InputDate<T>DateTime, DateOnly, DateTimeOffsetRenders <input type="date"> InputCheckboxboolRenders <input type="checkbox"> InputSelect<T>string, enums, numeric typesRenders <select> InputRadioGroup<T>string, enums, numeric typesWraps InputRadio<T> children InputFileIBrowserFileFile upload — interactive modes only
All input components use @bind-Value for binding. Always wrap text in a <label> or use id/for attributes for accessibility.
InputSelect with enum values <InputSelect @bind-Value="Model!.Status">
<option value="">-- Select --</option>
@foreach (var value in Enum.GetValues<OrderStatus>())
{
<option value="@value">@value</option>
}
</InputSelect>
InputRadioGroup <InputRadioGroup @bind-Value="Model!.Priority">
@foreach (var p in Enum.GetValues<Priority>())
{
<label>
<InputRadio Value="p" /> @p
</label>
}
</InputRadioGroup>
Validation
Data annotations Define validation rules on the model:
public class EmployeeModel
{
[Required, StringLength(100) ]
public string ? Name { get ; set ; }
[Required, EmailAddress ]
public string ? Email { get ; set ; }
[Range(18, 99) ]
public int Age { get ; set ; }
[Required ]
public string ? Department { get ; set ; }
}
Add <DataAnnotationsValidator /> inside EditForm — without it, annotation attributes are silently ignored.
<ValidationSummary /> — all errors in a list
<ValidationMessage For="() => Model!.FieldName" /> — per-field inline errors
Custom validator component For server-round-trip validation (uniqueness checks, business rules):
public class CustomValidator : ComponentBase
{
[CascadingParameter ]
private EditContext? EditContext { get ; set ; }
private ValidationMessageStore? messageStore;
protected override void OnInitialized ()
{
messageStore = new ValidationMessageStore(EditContext!);
EditContext!.OnValidationRequested += (s, e) => messageStore.Clear();
EditContext!.OnFieldChanged += (s, e) => messageStore.Clear(e.FieldIdentifier);
}
public void DisplayErrors (Dictionary<string , List<string >> errors )
{
foreach (var (field, messages) in errors)
{
foreach (var message in messages)
{
messageStore!.Add(EditContext!.Field(field), message);
}
}
EditContext!.NotifyValidationStateChanged();
}
public void ClearErrors ()
{
messageStore?.Clear();
EditContext?.NotifyValidationStateChanged();
}
}
<EditForm Model="Model" OnValidSubmit="HandleSubmit" FormName="register">
<DataAnnotationsValidator />
<CustomValidator @ref="customValidator" />
<ValidationSummary />
@* inputs *@
</EditForm>
@code {
private CustomValidator? customValidator;
private async Task HandleSubmit()
{
var errors = await RegistrationService.ValidateAsync(Model!);
if (errors.Count > 0)
{
customValidator!.DisplayErrors(errors);
return;
}
// proceed
}
}
React to Input Changes (Interactive Only)
@bind:after Run logic after a bound value changes:
<InputText @bind-Value="Model!.ZipCode" @bind:after="OnZipCodeChanged" />
@code {
private async Task OnZipCodeChanged()
{
// Fetch city/state based on new zip code
var location = await LocationService.LookupAsync(Model!.ZipCode);
Model.City = location?.City;
Model.State = location?.State;
}
}
@oninput for real-time filtering <input type="text" @oninput="OnSearchInput" placeholder="Search..." />
@code {
private string searchTerm = "";
private List<Item> filteredItems = new();
private void OnSearchInput(ChangeEventArgs e)
{
searchTerm = e.Value?.ToString() ?? "";
filteredItems = allItems.Where(i =>
i.Name.Contains(searchTerm, StringComparison.OrdinalIgnoreCase)).ToList();
}
}
SSR-Specific Patterns These apply when the form renders in Static SSR (mode = None, or per-page without @rendermode).
SupplyParameterFromForm Binds POST data to a property on form submission:
[SupplyParameterFromForm ]
private ContactModel? Contact { get ; set ; }
protected override void OnInitialized () => Contact ??= new ();
Critical: The ??= in OnInitialized is required. On GET the property is null — ??= creates the model. On POST the framework populates it — ??= preserves the posted values.
FormName — multiple forms on one page Each form needs a unique FormName:
<EditForm Model="Search" OnSubmit="DoSearch" FormName="search">...</EditForm>
<EditForm Model="Contact" OnValidSubmit="SaveContact" FormName="contact">...</EditForm>
Match [SupplyParameterFromForm] to its form:
[SupplyParameterFromForm(FormName = "search" ) ]
private SearchModel? Search { get ; set ; }
[SupplyParameterFromForm(FormName = "contact" ) ]
private ContactModel? Contact { get ; set ; }
Enhanced navigation for forms Add Enhance for SPA-like form submissions without full page reload:
<EditForm Model="Model" OnValidSubmit="Save" FormName="quick" Enhance>
Enhanced forms submit via fetch, patch the DOM, and preserve scroll position. The page stays interactive-feeling even in SSR.
Plain HTML forms When using raw <form> instead of EditForm in SSR, add the antiforgery token manually:
<form method="post" @onsubmit="Submit" @formname="raw-form">
<AntiforgeryToken />
<input name="Model.Name" value="@Model?.Name" />
<button type="submit">Send</button>
</form>
EditForm includes the antiforgery token automatically.
File Upload InputFile works in interactive modes only — not in Static SSR.
<InputFile OnChange="OnFileSelected" accept=".pdf,.jpg,.png" />
@code {
private IBrowserFile? selectedFile;
private async Task OnFileSelected(InputFileChangeEventArgs e)
{
selectedFile = e.File;
// Read stream with size limit
await using var stream = selectedFile.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024);
// Process stream — save to disk, upload to storage, etc.
}
}
Server: Default ~30 KB SignalR message size. Call OpenReadStream(maxAllowedSize) to increase. Large files stream over the circuit.
WebAssembly: File is read in the browser. No SignalR limit, but memory constrained.
<InputFile OnChange="OnFilesSelected" multiple />
@code {
private async Task OnFilesSelected(InputFileChangeEventArgs e)
{
foreach (var file in e.GetMultipleFiles(maxAllowedFiles: 10))
{
await using var stream = file.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024);
// Process each file
}
}
}
Prevent Double Submission Disable the submit button while processing:
<button type="submit" disabled="@isSubmitting">
@(isSubmitting ? "Saving..." : "Save")
</button>
@code {
private bool isSubmitting;
private async Task HandleSubmit()
{
isSubmitting = true;
try
{
await SaveService.SaveAsync(Model!);
}
finally
{
isSubmitting = false;
}
}
}
Custom Validation CSS Replace the default valid/invalid CSS classes:
public class BootstrapFieldCssClassProvider : FieldCssClassProvider
{
public override string GetFieldCssClass (EditContext editContext, in FieldIdentifier fieldIdentifier )
{
var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any();
return editContext.IsModified(fieldIdentifier)
? (isValid ? "is-valid" : "is-invalid" )
: "" ;
}
}
protected override void OnInitialized ()
{
editContext = new EditContext(model);
editContext.SetFieldCssClassProvider(new BootstrapFieldCssClassProvider());
}
Don'ts
Don't use @bind or @oninput in Static SSR forms — they require interactivity. Use [SupplyParameterFromForm] and FormName.
Don't forget Model ??= new() in OnInitialized — the model is null on GET, populated on POST.
Don't use OnSubmit together with OnValidSubmit/OnInvalidSubmit — they're mutually exclusive.
Don't omit <DataAnnotationsValidator /> — validation attributes are silently ignored without it.
Don't omit FormName in SSR when a page has multiple forms — both forms will fire on any submission.
Don't use InputFile in Static SSR — it requires an interactive render mode.
Don't use both Model and EditContext on an EditForm — pick one.
Don't forget <AntiforgeryToken /> in plain <form> elements — the server rejects the POST without it.