Skip to content

Commit fb16677

Browse files
leonsenftthePunderWoman
authored andcommitted
fix(forms): split the touched model into an input and touch output
The `touched` property was never meant to support two-way binding; a control should not be able to dictate that a field is no longer touched. * The `touched` input represents the touched state of the field. * The `touch` output allows a control implementation to indicate when the bound field is touched. Note the distinction is that the `touch` output indicates _when_ the field is touched, and not _whether_ the field is touched.
1 parent a3731a1 commit fb16677

File tree

4 files changed

+12
-11
lines changed

4 files changed

+12
-11
lines changed

goldens/public-api/forms/signals/index.api.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,8 @@ export interface FormUiControl {
258258
readonly pending?: InputSignal<boolean> | InputSignalWithTransform<boolean, unknown>;
259259
readonly readonly?: InputSignal<boolean> | InputSignalWithTransform<boolean, unknown>;
260260
readonly required?: InputSignal<boolean> | InputSignalWithTransform<boolean, unknown>;
261-
readonly touched?: ModelSignal<boolean> | InputSignal<boolean> | InputSignalWithTransform<boolean, unknown> | OutputRef<boolean>;
261+
readonly touch?: OutputRef<void>;
262+
readonly touched?: InputSignal<boolean> | InputSignalWithTransform<boolean, unknown>;
262263
}
263264

264265
// @public

packages/forms/signals/src/api/control.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,7 @@ export interface FormUiControl {
6161
* An input to receive the touched status for the field. If implemented, the `Field` directive
6262
* will automatically bind the touched status from the bound field to this input.
6363
*/
64-
readonly touched?:
65-
| ModelSignal<boolean>
66-
| InputSignal<boolean>
67-
| InputSignalWithTransform<boolean, unknown>
68-
| OutputRef<boolean>;
69-
64+
readonly touched?: InputSignal<boolean> | InputSignalWithTransform<boolean, unknown>;
7065
/**
7166
* An input to receive the dirty status for the field. If implemented, the `Field` directive
7267
* will automatically bind the dirty status from the bound field to this input.
@@ -117,6 +112,10 @@ export interface FormUiControl {
117112
readonly pattern?:
118113
| InputSignal<readonly RegExp[]>
119114
| InputSignalWithTransform<readonly RegExp[], unknown>;
115+
/**
116+
* An output to emit when the control is touched.
117+
*/
118+
readonly touch?: OutputRef<void>;
120119
/**
121120
* Focuses the UI control.
122121
*

packages/forms/signals/src/directive/control_custom.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export function customControlCreate(
2323
parent: FormField<unknown>,
2424
): () => void {
2525
host.listenToCustomControlModel((value) => parent.state().controlValue.set(value));
26-
host.listenToCustomControlOutput('touchedChange', () => parent.state().markAsTouched());
26+
host.listenToCustomControlOutput('touch', () => parent.state().markAsTouched());
2727

2828
parent.registerAsBinding(host.customControl as FormUiControl);
2929

packages/forms/signals/test/web/form_field_directive.spec.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4099,11 +4099,12 @@ describe('field directive', () => {
40994099
template: '<input #i [value]="value()" (input)="value.set(i.value)" />',
41004100
})
41014101
class CustomInput implements FormValueControl<string> {
4102-
value = model('');
4103-
touched = model(false);
4102+
readonly value = model('');
4103+
readonly touched = input(false);
4104+
readonly touch = output<void>();
41044105

41054106
touchIt() {
4106-
this.touched.set(true);
4107+
this.touch.emit();
41074108
}
41084109
}
41094110

0 commit comments

Comments
 (0)