# matchFields

> Every named control must hold the same value — the group-level counterpart to match, with no ordering hole.

Source: https://ngwr.dev/reference/validators/match-fields  
Kind: Validator

## Live demo

This form is prefilled with two values that disagree. `form.errors` carries the group-level result from the moment the group was constructed — before any rendering. `confirm.errors` carries `match`'s result too, because `formControlName` revalidated the control when it bound; construct the same group without rendering it and only the group-level one is there.

## Usage

```angular-ts
import { FormControl, FormGroup } from '@angular/forms';
import { WrValidators } from 'ngwr/validators';

const form = new FormGroup(
  {
    password: new FormControl(''),
    confirm:  new FormControl(''),
  },
  { validators: [WrValidators.matchFields('password', 'confirm')] },
);
```

## Why this exists

Angular runs a control's validators inside the control's own constructor, before it has been attached to a parent. A cross-field validator on the CHILD — like `match` — therefore looks up a sibling that is not reachable yet, returns null, and is never re-run by the act of joining the group. `formControlName` revalidates when it binds, so a rendered form corrects itself on its first change detection; anything that reads validity before that does not. A route guard or resolver checking `form.valid`, a form built and submitted from a service, a spec — all see a group that is plainly mismatched and plainly valid. A validator on the GROUP has no such window: Angular attaches the children and aggregates their values before it runs the group\'s own validators, so it is right the moment the group exists.

## Pair it with match

`<wr-form-field>` renders messages for the one control projected into it, so a group-level error is not something it can show. Run both: `matchFields` on the group makes the form correct from the first frame, and `match` on the confirm control supplies the per-field sentence once the field is touched. They do not conflict — different error keys, different hosts.

```angular-ts
const form = new FormGroup(
  {
    password: new FormControl(''),
    // match — gives the confirm field its own message, once touched
    confirm:  new FormControl('', [WrValidators.match('password')]),
  },
  // matchFields — makes the form correct from the first frame
  { validators: [WrValidators.matchFields('password', 'confirm')] },
);
```

## Paths and arity

Names are `AbstractControl.get()` paths, so nested groups and `FormArray` indexes work with no special handling. Pass more than two names and every one is compared against the first.

```angular-ts
// Names are AbstractControl.get() paths.
WrValidators.matchFields('billing.zip', 'shipping.zip');

// N-ary: every name is compared against the first.
WrValidators.matchFields('a', 'b', 'c');
```

## API

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `signature` | Factory — call with two or more control names. Attach the result to the GROUP that owns them. | `(first: string, second: string, ...rest: readonly string[]) => ValidatorFn` | `—` |
| `lookup` | Names are `AbstractControl.get()` paths, so `billing.zip` reaches into a nested group and `items.0` into a `FormArray`. Name a LEAF: a name that resolves to a group or an array is refused too, because comparing two containers by reference can never succeed. Either case turns the whole check off — never a partial comparison — and warns once per validator instance in dev. | `—` | `—` |
| `error key` | On mismatch: `{ matchFields: { fields: [...] } }`, on the GROUP. The payload echoes the configured names and deliberately carries no control values. | `{ matchFields: { fields } }` | `—` |
| `empty values` | `null`, `undefined` and `""` count as the same empty value. All empty passes; one filled against one empty reports. | `—` | `—` |
| `comparison` | Strict `===`, so two `Date` objects for the same instant are NOT equal — compare a derived primitive instead. | `—` | `—` |
| `disabled controls` | Skipped, so the rule compares exactly what `group.value` contains. Fewer than two enabled names short-circuits to valid. | `—` | `—` |

## See also

- [match](https://ngwr.dev/reference/validators/match) — The child-level half of the pairing — what gives a field its message.
- [wr-form-field](https://ngwr.dev/reference/components/form-field) — Renders a message per error key — for the one control projected into it.
