useControlUntouched
Returns whether an AbstractControl is untouched (has not been interacted with) as a signal. The signal updates reactively whenever the control's untouched state changes. Works with FormControl, FormGroup, and FormArray.
Usage
typescript
import { useControlUntouched } from 'ng-reactive-utils';
@Component({
template: `
<input [formControl]="emailControl" />
@if (isUntouched()) {
<span class="hint">Click to enter your email</span>
}
`,
})
class EmailFieldComponent {
emailControl = new FormControl('');
isUntouched = useControlUntouched(this.emailControl);
}Advanced Usage
typescript
import { useControlUntouched } from 'ng-reactive-utils';
@Component({
template: `
<div class="form-field" [class.pristine]="isUntouched()">
<label>Password</label>
<input type="password" [formControl]="passwordControl" />
@if (isUntouched()) {
<div class="requirements">Password must be at least 8 characters</div>
} @else if (passwordControl.invalid) {
<div class="error">Please enter a valid password</div>
}
</div>
`,
})
class PasswordFieldComponent {
passwordControl = new FormControl('', [Validators.minLength(8)]);
isUntouched = useControlUntouched(this.passwordControl);
}Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
control | AbstractControl | required | The control to check untouched state for |
Returns
Signal<boolean> - A readonly signal containing the untouched state (true if not interacted with)
Notes
- Works with FormControl, FormGroup, and FormArray
- Uses
control.events(Angular's unified event stream) filtered toTouchedChangeEvent— notstatusChanges, which does not emit on touched-state changes - Returns
truewhen the control has not been blurred or programmatically touched - Returns
falseoncemarkAsTouched()is called or the control loses focus - Opposite of
useControlTouched - Useful for showing hints before user interaction
Source
ts
import { Signal } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { AbstractControl, TouchedChangeEvent } from '@angular/forms';
import { filter, map } from 'rxjs';
/**
* Returns whether an AbstractControl is untouched (has not been interacted with) as a signal.
* The signal updates reactively whenever the control's untouched state changes,
* including when markAsTouched() or markAsUntouched() are called directly.
* Works with FormControl, FormGroup, and FormArray.
*
* @param control - The AbstractControl to check untouched state for
* @returns A signal containing the untouched state (true if not interacted with)
*
* @example
* ```typescript
* @Component({
* template: `
* <input [formControl]="emailControl" />
* @if (isUntouched()) {
* <span>Please fill out this field</span>
* }
* `
* })
* class MyComponent {
* emailControl = new FormControl('');
* isUntouched = useControlUntouched(this.emailControl);
* }
* ```
*/
export const useControlUntouched = (control: AbstractControl): Signal<boolean> => {
return toSignal(
control.events.pipe(
filter((event): event is TouchedChangeEvent => event instanceof TouchedChangeEvent),
map((event) => !event.touched),
),
{ initialValue: control.untouched },
) as Signal<boolean>;
};