8000 add default touched event logic for ui controls (#61288) · alxhub/angular@c37bf3f · GitHub
[go: up one dir, main page]

Skip to content

Commit c37bf3f

Browse files
mmalerbaalxhub
authored andcommitted
add default touched event logic for ui controls (angular#61288)
1 parent a2a6488 commit c37bf3f

File tree

1 file changed

+19
-2
lines changed
  • packages/forms/experimental/src/controls

1 file changed

+19
-2
lines changed

packages/forms/experimental/src/controls/field.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
InputSignal,
1919
OutputEmitterRef,
2020
OutputRef,
21+
OutputRefSubscription,
2122
untracked,
2223
} from '@angular/core';
2324
import {ControlValueAccessor, NG_VALUE_ACCESSOR, NgControl} from '@angular/forms';
@@ -38,7 +39,7 @@ import {InteropNgControl} from './interop_ng_control';
3839
export class FieldDirective<T> {
3940
readonly injector = inject(Injector);
4041
readonly field = input.required<Field<T>>();
41-
readonly el = inject(ElementRef);
42+
readonly el: ElementRef<HTMLElement> = inject(ElementRef);
4243
readonly cvaArray = inject<ControlValueAccessor[]>(NG_VALUE_ACCESSOR, {optional: true});
4344

4445
private _ngControl: InteropNgControl | undefined;
@@ -107,12 +108,28 @@ export class FieldDirective<T> {
107108
const cleanupValue = cmp.value.subscribe((newValue) =>
108109
this.field().$state.value.set(newValue),
109110
);
110-
const cleanupTouch = cmp.touch?.subscribe(() => this.field().$state.markAsTouched());
111+
let cleanupTouch: OutputRefSubscription | undefined;
112+
let cleanupDefaultTouch: (() => void) | undefined;
113+
if (cmp.touch !== undefined) {
114+
cleanupTouch = cmp.touch.subscribe(() => this.field().$state.markAsTouched());
115+
} else {
116+
// If the component did not give us a touch event stream, use the standard touch logic,
117+
// marking it touched when the focus moves from inside the host element to outside.
118+
const listener = (event: FocusEvent) => {
119+
const newActiveEl = event.relatedTarget;
120+
if (!this.el.nativeElement.contains(newActiveEl as Element | null)) {
121+
this.field().$state.markAsTouched();
122+
}
123+
};
124+
this.el.nativeElement.addEventListener('focusout', listener);
125+
cleanupDefaultTouch = () => this.el.nativeElement.removeEventListener('focusout', listener);
126+
}
111127

112128
// Cleanup for output binding subscriptions:
113129
injector.get(DestroyRef).onDestroy(() => {
114130
cleanupValue.unsubscribe();
115131
cleanupTouch?.unsubscribe();
132+
cleanupDefaultTouch?.();
116133
});
117134
} else {
118135
throw new Error(`Unhandled control?`);

0 commit comments

Comments
 (0)
0