AgentSkillsCN

Blazor Standards

Blazor标准

SKILL.md

Blazor Development Standards

Use these standards when writing Blazor components in the ProcurementHub project.

Component Library Priority

Only relevant if the corresponding library is already included in the project.

  1. WssBlazorControls - Primary choice for forms and inputs. Also reference edit-controls.md for detailed usage.
  2. AntBlazor - Limited to: Modal, Multiselect, Popconfirm, Notification
  3. Native HTML - When EditControls are insufficient

Form Controls

razor
<!-- Primary Controls -->
<EditString @bind-Value="model.Name" Field="@(() => model.Name)" />
<EditTextArea @bind-Value="model.Description" Field="@(() => model.Description)" />
<EditNumber @bind-Value="model.Amount" Field="@(() => model.Amount)" Format="C" />
<EditBool @bind-Value="model.IsActive" Field="@(() => model.IsActive)" />
<EditDate @bind-Value="model.Date" Field="@(() => model.Date)" />
<EditSelectEnum @bind-Value="model.Status" Field="@(() => model.Status)" Type="typeof(Status)" />

<!-- Specialized Controls -->
<EditBoolNullRadio @bind-Value="model.IsApproved" Field="@(() => model.IsApproved)" />
<EditRadioEnum @bind-Value="model.Priority" Field="@(() => model.Priority)" Type="typeof(Priority)" />
<EditCheckedStringList @bind-Value="model.Tags" Field="@(() => model.Tags)" Options="tagOptions" />

Layout Patterns

razor
<!-- Vertical Layout -->
<div class="flex-column-16">
    <EditString @bind-Value="model.Name" Field="@(() => model.Name)" />
    <EditTextArea @bind-Value="model.Description" Field="@(() => model.Description)" />
</div>

<!-- Horizontal Layout -->
<div class="flex-row-16">
    <EditString @bind-Value="model.FirstName" Field="@(() => model.FirstName)" />
    <EditString @bind-Value="model.LastName" Field="@(() => model.LastName)" />
</div>

<!-- Accessible Form Groups (6+ fields or complex forms) -->
<fieldset class="card flex-column-16">
    <legend>Contact Information</legend>
    <EditString @bind-Value="model.Email" Field="@(() => model.Email)" />
    <EditString @bind-Value="model.Phone" Field="@(() => model.Phone)" />
</fieldset>

Component Code Organization

Code-Behind Pattern (Use [Inject] Properties, NOT Constructor Injection)

csharp
// Component.razor.cs - Keep logic separate from markup
namespace ProcurementHub.Client.Components;

public partial class Component
{
    [Inject] MyService MyService { get; set; } = null!;

    [Parameter] public Model Data { get; set; } = new();
    [Parameter] public bool IsReadOnly { get; set; }

    async Task HandleSubmit()
    {
        // Business logic here
    }
}

Two-Way Binding Pattern

csharp
public partial class CustomInput
{
    [Parameter] public string Value { get; set; } = string.Empty;
    [Parameter] public EventCallback<string> ValueChanged { get; set; }

    async Task OnValueChanged(string newValue)
    {
        Value = newValue;
        await ValueChanged.InvokeAsync(Value);
    }
}

Blazor Best Practices Checklist

  • Single-file components - Combine .razor and .razor.cs when total < 60 lines; only separate when ≥ 60 lines or complex logic
  • Code-behind files - Separate .razor.cs for logic when combined .razor file would exceed 60 lines; combine for smaller components
  • [Inject] properties - Use [Inject] for dependency injection, NOT constructor injection
  • EditControls first - Use WssBlazorControls before alternatives
  • Two-way binding - Implement @bind-Value pattern for custom components
  • Use existing CSS - Check app.css, edit-controls.css, ant-overrides.css first
  • Consistent spacing - flex-column-16 vertical, flex-row-16 horizontal
  • CSS isolation - Component-specific styles in .razor.css files
  • Accessibility - Use fieldset/legend for form groups (6+ fields or complex forms)
  • Concise markup - Use ternary operators and helper methods
  • Method groups - Use @onclick="Toggle" instead of lambda wrappers
  • Avoid code in markup - Keep logic in code-behind files
  • Comment nested sections - Add comments <!-- --> for nested markup
  • Remove unnecessary parameters - Only pass what is needed to child components
  • Remove unnecessary nesting - If a div and it's parent div can be combined, do so.
  • Constructor injection - Use [Inject] properties instead for Blazor components