Skip to content

Commit 40f7fbd

Browse files
whit33ythePunderWoman
authored andcommitted
docs: clarify value attribute on radio/checkbox inputs is allowed with formField
1 parent 58efd86 commit 40f7fbd

1 file changed

Lines changed: 37 additions & 27 deletions

File tree

skills/dev-skills/angular-developer/references/signal-forms.md

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,20 @@ Do _NOT_ bind the `name` field.
170170
When using `[formField]`, you MUST NOT set the following attributes in the template (either static or bound):
171171

172172
- `min`, `max` (Use validators in the schema instead)
173-
- `value`, `[value]`, `[attr.value]` (Already handled by `[formField]`)
173+
- `value`, `[value]`, `[attr.value]` on **text/number/date inputs** (Already handled by `[formField]`)
174174
- `[attr.min]`, `[attr.max]`
175175
- `[disabled]`, `[readonly]` (Already handled by `[formField]`)
176176

177+
**Exception**: Static `value` on `<input type="radio">` and `<input type="checkbox">` is **allowed and required** — it identifies which option the input represents, not the bound field value.
178+
179+
```html
180+
<!-- CORRECT: value on radio specifies which option this button represents -->
181+
<input type="radio" value="economy" [formField]="bookingForm.package.tier" />
182+
183+
<!-- WRONG: value binding on a regular input -->
184+
<input [value]="someVar" [formField]="form.name" />
185+
```
186+
177187
Do NOT do this: `<input min="1" [formField]>` or `<input [value]="val" [formField]>`.
178188

179189
```html
@@ -506,32 +516,32 @@ form(
506516

507517
## Common Pitfalls (DO NOT DO THESE)
508518

509-
| Error Scenario | WRONG (Common Mistake) | RIGHT (Correct Way) |
510-
| :--------------------- | :-------------------------------------------- | :---------------------------------------------------------- |
511-
| **Accessing Flags** | `form.field.valid()` | `form.field().valid()` |
512-
| **Accessing value** | `form.field.value()` | `form.field().value()` |
513-
| **Setting value** | `form.field.set(x)` | Update model signal: `this.model.update(...)` |
514-
| **Form root flags** | `form.invalid()` | `form().invalid()` |
515-
| **Double-calling** | `form.field()()` | `form.field().value()` |
516-
| **Rules Context** | `({ touched }) => touched()` | `({ state }) => state.touched()` |
517-
| **Calling Paths** | `applyWhen(p.foo, () => p.foo() === 'x')` | `applyWhen(p.foo, ({ valueOf }) => valueOf(p.foo) === 'x')` |
518-
| **applyWhen args** | `applyWhen(condition, () => {...})` | `applyWhen(path, condition, schemaFn)` - needs 3 args |
519-
| **Array length** | `form.items().length` | `form.items.length` (structural) |
520-
| **Multi-select array** | `<select [formField]="form.tags">` (string[]) | Use checkboxes for array fields |
521-
| **readonly attribute** | `<input readonly [formField]>` | Use `readonly()` rule in schema |
522-
| **min/max attributes** | `<input min="1" max="10">` | Use `min()` and `max()` rules in schema |
523-
| **value binding** | `<input [value]="val">` | Do NOT use `[value]` with `[formField]` |
524-
| **when option** | `pattern(p.x, /.../, {when: ...})` | `when` only works with `required()` |
525-
| **Submit callback** | `submit(form, () => { ... })` | `submit(form, async () => { ... })` |
526-
| **Async params** | `params: s.field` | `params: ({ value }) => value()` |
527-
| **Async onError** | Omitting `onError` | `onError` is REQUIRED in `validateAsync` |
528-
| **resource() API** | `request: signal` | `params: signal` |
529-
| **applyEach args** | `applyEach(s.items, (item, index) => ...)` | `applyEach(s.items, (item) => ...)` |
530-
| **Nested @for** | `$parent.$index` | Use `let outerIndex = $index` |
531-
| **FormState import** | `import { FormState }` | `FormState` does not exist, use `FieldState` |
532-
| **Null in model** | `signal({ name: null })` | `signal({ name: '' })` or `signal({ age: 0 })` |
533-
| **Validate syntax** | `validate(s.field, { value } => ...)` | `validate(s.field, ({ value }) => ...)` |
534-
| **Checkbox Array** | `[formField]="form.tags"` (string[]) | Checkboxes ONLY bind to `boolean` |
519+
| Error Scenario | WRONG (Common Mistake) | RIGHT (Correct Way) |
520+
| :--------------------- | :-------------------------------------------- | :------------------------------------------------------------------------------- |
521+
| **Accessing Flags** | `form.field.valid()` | `form.field().valid()` |
522+
| **Accessing value** | `form.field.value()` | `form.field().value()` |
523+
| **Setting value** | `form.field.set(x)` | Update model signal: `this.model.update(...)` |
524+
| **Form root flags** | `form.invalid()` | `form().invalid()` |
525+
| **Double-calling** | `form.field()()` | `form.field().value()` |
526+
| **Rules Context** | `({ touched }) => touched()` | `({ state }) => state.touched()` |
527+
| **Calling Paths** | `applyWhen(p.foo, () => p.foo() === 'x')` | `applyWhen(p.foo, ({ valueOf }) => valueOf(p.foo) === 'x')` |
528+
| **applyWhen args** | `applyWhen(condition, () => {...})` | `applyWhen(path, condition, schemaFn)` - needs 3 args |
529+
| **Array length** | `form.items().length` | `form.items.length` (structural) |
530+
| **Multi-select array** | `<select [formField]="form.tags">` (string[]) | Use checkboxes for array fields |
531+
| **readonly attribute** | `<input readonly [formField]>` | Use `readonly()` rule in schema |
532+
| **min/max attributes** | `<input min="1" max="10">` | Use `min()` and `max()` rules in schema |
533+
| **value binding** | `<input [value]="val">` | Do NOT use `[value]` with `[formField]` (static `value` on radio/checkbox is OK) |
534+
| **when option** | `pattern(p.x, /.../, {when: ...})` | `when` only works with `required()` |
535+
| **Submit callback** | `submit(form, () => { ... })` | `submit(form, async () => { ... })` |
536+
| **Async params** | `params: s.field` | `params: ({ value }) => value()` |
537+
| **Async onError** | Omitting `onError` | `onError` is REQUIRED in `validateAsync` |
538+
| **resource() API** | `request: signal` | `params: signal` |
539+
| **applyEach args** | `applyEach(s.items, (item, index) => ...)` | `applyEach(s.items, (item) => ...)` |
540+
| **Nested @for** | `$parent.$index` | Use `let outerIndex = $index` |
541+
| **FormState import** | `import { FormState }` | `FormState` does not exist, use `FieldState` |
542+
| **Null in model** | `signal({ name: null })` | `signal({ name: '' })` or `signal({ age: 0 })` |
543+
| **Validate syntax** | `validate(s.field, { value } => ...)` | `validate(s.field, ({ value }) => ...)` |
544+
| **Checkbox Array** | `[formField]="form.tags"` (string[]) | Checkboxes ONLY bind to `boolean` |
535545

536546
## Big Form Example
537547

0 commit comments

Comments
 (0)