Files
Htmx/docs/Components/Checkbox.md
T
2026-05-04 19:57:48 +05:00

208 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Checkbox
A styled checkbox input with an optional visible label. Uses the `accent-primary` Tailwind class so the checkmark color follows your primary theme color.
---
## HTML structure
```
div.flex.items-center.space-x-2
input[type=checkbox, id, name, value, class, $$Checked$$]
label[for={id}] ← omitted when label is empty
{label text}
```
---
## CSS mechanics
| Class | Effect |
|---|---|
| `accent-primary` | Checkmark color follows the `--color-primary` CSS variable |
| `h-4 w-4 rounded` | Consistent 16×16 size with slightly rounded corners |
| `cursor-pointer` | Pointer cursor on label |
| `text-sm font-medium leading-none peer-disabled:opacity-70` | Standard label styling |
---
## Constructor signature
```csharp
public Checkbox(
string id,
string label = "",
string name = "",
string value = "true",
bool @checked = false)
```
| Parameter | Description |
|---|---|
| `id` | Element id and the `for` attribute on the label |
| `label` | Visible text next to the checkbox; omit for a standalone checkbox |
| `name` | Form field name (required when used in a form) |
| `value` | Submitted value when checked (default: `"true"`) |
| `checked` | Pre-checked state |
---
## Usage examples
### Basic opt-in checkbox
```csharp
new Checkbox(
id: "newsletter",
label: "Subscribe to newsletter",
name: "newsletter")
```
### Pre-checked
```csharp
new Checkbox(
id: "remember",
label: "Remember me",
name: "rememberMe",
checked: true)
```
### No visible label
```csharp
new Checkbox(id: "select-all", name: "selectAll")
```
### Custom submitted value
```csharp
new Checkbox(
id: "agree",
label: "I agree to the terms",
name: "terms",
value: "accepted")
```
### Reading in a form handler
```csharp
public record Command(
[property: FromForm] string? Newsletter = null, // null when unchecked
[property: FromForm] string? RememberMe = null
);
bool wantsNewsletter = command.Newsletter == "true";
bool rememberUser = command.RememberMe == "true";
```
> Note: Unchecked checkboxes are not included in form data. Always use a nullable string or a default value of `null`.
---
## Tips and tricks
- Because HTML forms only submit checked checkboxes, pair a checkbox with a hidden input of the same name and value `"false"` if you need the unchecked state explicitly in your command.
- The label `for` attribute ties to the `id`, so clicking the label text toggles the checkbox — always set `id`.
- If you need multi-select (select multiple rows in a table), use the same `name` for all checkboxes; they will be submitted as a comma-separated list or multiple values depending on form binding.
- `accent-primary` is a modern CSS property — all current browsers support it.
- `accent-primary` is a modern CSS property — all current browsers support it.
---
## Complete page example
**`Templates/PreferencesPage.htmx`**
```html
<div class="max-w-md mx-auto py-10">
<h1 class="text-2xl font-bold mb-6">Preferences</h1>
<form method="post" action="/preferences">
$$AntiforgeryToken$$
<div class="space-y-4 mb-6">
$$NewsletterCheck$$
$$MarketingCheck$$
$$RememberCheck$$
</div>
$$SaveBtn$$
</form>
$$SuccessAlert$$
</div>
```
**`Templates/PreferencesPage.htmx.cs`**
```csharp
namespace Htmx.ApiDemo.Templates;
public sealed class PreferencesPage : PreferencesPageBase
{
private readonly IHtmxComponent _newsletter;
private readonly IHtmxComponent _marketing;
private readonly IHtmxComponent _remember;
private readonly IHtmxComponent _save;
private readonly IHtmxComponent _success;
private readonly byte[] _afToken;
public PreferencesPage(
IAntiforgery af,
HttpContext ctx,
UserPrefs? prefs = null,
bool saved = false)
{
var tokens = af.GetAndStoreTokens(ctx);
_afToken = $"""<input type="hidden" name="{tokens.FormFieldName}" value="{tokens.RequestToken}">""".ToUtf8Bytes();
_newsletter = new Components.Checkbox(
id: "newsletter", label: "Receive newsletter",
name: "newsletter", @checked: prefs?.Newsletter ?? false);
_marketing = new Components.Checkbox(
id: "marketing", label: "Receive marketing emails",
name: "marketing", @checked: prefs?.Marketing ?? false);
_remember = new Components.Checkbox(
id: "remember", label: "Keep me signed in",
name: "remember", @checked: prefs?.Remember ?? false);
_save = new Components.Button("Save preferences", type: "submit");
_success = saved
? new Components.Alert(title: "Preferences saved.")
: HtmxEmpty.Instance;
}
protected override void RenderAntiforgeryToken(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_afToken);
protected override void RenderNewsletterCheck(HtmxRenderContext ctx) => _newsletter.Render(ctx.Next());
protected override void RenderMarketingCheck(HtmxRenderContext ctx) => _marketing.Render(ctx.Next());
protected override void RenderRememberCheck(HtmxRenderContext ctx) => _remember.Render(ctx.Next());
protected override void RenderSaveBtn(HtmxRenderContext ctx) => _save.Render(ctx.Next());
protected override void RenderSuccessAlert(HtmxRenderContext ctx) => _success.Render(ctx.Next());
}
```
**POST handler**
```csharp
[Handler]
[MapPost("/preferences")]
public static partial class PostPreferencesHandler
{
public record Command(
[property: FromForm] string? Newsletter = null,
[property: FromForm] string? Marketing = null,
[property: FromForm] string? Remember = null);
private static Task<IResult> HandleAsync(
[AsParameters] Command cmd, HttpContext ctx, IAntiforgery af, CancellationToken ct)
{
var prefs = new UserPrefs(
Newsletter: cmd.Newsletter != null,
Marketing: cmd.Marketing != null,
Remember: cmd.Remember != null);
// Persist prefs…
return ctx.WriteHtmxPage(new PreferencesPage(af, ctx, prefs, saved: true), title: "Preferences");
}
}
public record UserPrefs(bool Newsletter, bool Marketing, bool Remember);
```
**`AppJsonSerializerContext.cs`**
```csharp
[JsonSerializable(typeof(PostPreferencesHandler.Command), TypeInfoPropertyName = "PreferencesCommand")]
```