Live demo
Usage
import { FormControl, FormGroup } from '@angular/forms';
import { WrValidators } from 'ngwr/validators';
const form = new FormGroup({
password: new FormControl(''),
confirm: new FormControl('', [WrValidators.match('password')]),
});Why ngwr provides this
Cross-field equality (password confirmation) is awkward with built-ins — every project ends up hand-writing the same group-level validator. This one reads the sibling control by name.
It cannot see a mismatch that was there from the start
A control runs its validators inside its own constructor, before it has been attached to a parent — so the sibling lookup finds nothing and this returns null, and joining the group does not re-run it. formControlName revalidates when it binds, so a form rendered through the reactive-forms directives corrects itself on its first change detection. The window is open for whatever reads validity FIRST — a route guard or resolver checking form.valid, a form built and submitted from a service, a unit test. Put matchFields on the group to close it and keep this one on the child for the per-field message; they use different error keys and do not conflict.
API
| Name | Description | Type | Default |
|---|---|---|---|
signature | Factory — call with the sibling control name. | (name: string) => ValidatorFn | — |
lookup | Looks up the sibling via control.parent.get(name). Returns null when the parent or sibling is missing. | — | — |
error key | On mismatch: { match: { target: 'password' } }. There is no empty guard — a filled value against an empty one reports. | { match: { target } } | — |
initial values | Cannot report a mismatch until something revalidates the control — a control runs its validators before it has a parent, so the sibling lookup finds nothing. formControlName revalidates when it binds; a guard or a service reading form.valid first does not. Put matchFields on the group to close that window. | — | — |