# ValidationGroup

```ts
import { ValidationGroup } from 'cx/widgets';
```

The `ValidationGroup` is a container that tracks the validation state of all form fields inside it. It reports whether all fields are valid and can propagate settings like `disabled`, `readOnly`, or `viewMode` to all child fields.

```tsx
import { createModel } from "cx/data";
import { expr, LabelsTopLayout } from "cx/ui";
import { Button, MsgBox, TextField, ValidationGroup } from "cx/widgets";

interface Model {
  data: {
    firstName: string;
    lastName: string;
    email: string;
  };
  valid: boolean;
  visited: boolean;
}

const m = createModel<Model>();

export default (
  <div class="flex flex-col gap-4">
    <ValidationGroup valid={m.valid} visited={m.visited} asterisk>
      <LabelsTopLayout vertical>
        <TextField label="First Name" value={m.data.firstName} required />
        <TextField label="Last Name" value={m.data.lastName} required />
        <TextField label="Email" value={m.data.email} required />
      </LabelsTopLayout>
    </ValidationGroup>
    <div class="flex items-center gap-4">
      <Button
        onClick={(e, { store }) => {
          if (!store.get(m.valid)) {
            store.set(m.visited, true);
            return;
          }
          MsgBox.alert("Form submitted successfully!");
        }}
      >
        Submit
      </Button>
      <span
        class="text-sm"
        text={expr(m.valid, (v) =>
          v ? "Form is valid ✓" : "Please fill all required fields",
        )}
      />
    </div>
  </div>
);

```

## Error Summary

The `errors` binding collects all validation errors from child fields. Use it with a `Repeater` to display an error summary.

```tsx
import { createModel } from "cx/data";
import { falsy, LabelsTopLayout, truthy } from "cx/ui";
import {
  enableTooltips,
  ValidationGroup,
  NumberField,
  Validator,
  Repeater,
  type ValidationErrorData,
} from "cx/widgets";

enableTooltips();

interface Model {
  x: number;
  y: number;
  valid: boolean;
  errors: ValidationErrorData[];
  $error: ValidationErrorData;
}

const m = createModel<Model>();

export default (
  <div>
    <div class="mb-3">Enter X and Y so that X + Y = 20.</div>
    <div
      class="flex gap-16 pl-4 border-2 rounded p-4 items-center"
      className={{
        "border-red-600": falsy(m.valid),
        "border-green-500": truthy(m.valid),
      }}
    >
      <ValidationGroup valid={m.valid} errors={m.errors} visited>
        <LabelsTopLayout class="-mt-4">
          <NumberField
            label="X"
            value={m.x}
            required
            requiredText="Please enter X."
            style="width: 80px"
          />
          <NumberField
            label="Y"
            value={m.y}
            required
            requiredText="Please enter Y."
            style="width: 80px"
          />
        </LabelsTopLayout>
        <Validator
          value={{ x: m.x, y: m.y }}
          onValidate={({ x, y }) => {
            if (x + y !== 20) return "X + Y must equal 20";
          }}
          visited
        />
      </ValidationGroup>
      <ul class="text-red-700 text-sm" visible={falsy(m.valid)}>
        <Repeater records={m.errors} recordAlias={m.$error}>
          <li text={m.$error.message} class="list-disc!" />
        </Repeater>
      </ul>
      <div class="text-green-600 text-sm" visible={truthy(m.valid)}>
        Form is valid!
      </div>
    </div>
  </div>
);

```

## Configuration

### Validation State

| Property   | Type                    | Description                                                        |
| ---------- | ----------------------- | ------------------------------------------------------------------ |
| `valid`    | `boolean`               | Binding set to `true` if all child fields are valid                |
| `invalid`  | `boolean`               | Binding set to `true` if any child field has a validation error    |
| `errors`   | `ValidationErrorData[]` | Binding to store the array of validation errors                    |
| `visited`  | `boolean`               | If `true`, forces all children to show validation errors           |
| `isolated` | `boolean`               | If `true`, isolates children from outer validation scopes          |

### Field State Propagation

| Property        | Type      | Default | Description                                              |
| --------------- | --------- | ------- | -------------------------------------------------------- |
| `disabled`      | `boolean` | `false` | Disables all inner elements that support `disabled`      |
| `enabled`       | `boolean` |         | Opposite of `disabled`                                   |
| `readOnly`      | `boolean` | `false` | Makes all inner elements read-only                       |
| `viewMode`      | `boolean` | `false` | Switches all inner fields to view mode                   |
| `strict`        | `boolean` | `false` | Forces children to respect group-level flags             |
| `asterisk`      | `boolean` | `false` | Adds red asterisk for all required fields                |
| `tabOnEnterKey` | `boolean` | `false` | Moves focus to the next field when Enter is pressed      |