{"id":14520367,"url":"https://github.com/ngx-vest-forms/ngx-vest-forms","last_synced_at":"2026-04-01T23:16:53.836Z","repository":{"id":243486890,"uuid":"811415257","full_name":"ngx-vest-forms/ngx-vest-forms","owner":"ngx-vest-forms","description":"Simple form development for complex and scalable solutions","archived":false,"fork":false,"pushed_at":"2026-03-24T11:05:35.000Z","size":20845,"stargazers_count":57,"open_issues_count":8,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2026-03-25T13:22:16.137Z","etag":null,"topics":["angular","template-driven-forms","validation","vestjs"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ngx-vest-forms.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-06-06T14:54:23.000Z","updated_at":"2026-03-24T11:05:40.000Z","dependencies_parsed_at":"2024-06-12T12:18:55.572Z","dependency_job_id":"3a415400-fadb-46d5-9667-2ff9f41f2999","html_url":"https://github.com/ngx-vest-forms/ngx-vest-forms","commit_stats":null,"previous_names":["simplifiedcourses/simplified-forms","simplifiedcourses/ngx-vest-forms","ngx-vest-forms/ngx-vest-forms"],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/ngx-vest-forms/ngx-vest-forms","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngx-vest-forms%2Fngx-vest-forms","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngx-vest-forms%2Fngx-vest-forms/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngx-vest-forms%2Fngx-vest-forms/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngx-vest-forms%2Fngx-vest-forms/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ngx-vest-forms","download_url":"https://codeload.github.com/ngx-vest-forms/ngx-vest-forms/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngx-vest-forms%2Fngx-vest-forms/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31292866,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T21:15:39.731Z","status":"ssl_error","status_checked_at":"2026-04-01T21:15:34.046Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","template-driven-forms","validation","vestjs"],"created_at":"2024-09-04T04:01:39.713Z","updated_at":"2026-04-01T23:16:53.823Z","avatar_url":"https://github.com/ngx-vest-forms.png","language":"TypeScript","funding_links":[],"categories":["Third Party Components","Recently Updated"],"sub_categories":["Forms","[Sep 02, 2024](/content/2024/09/02/README.md)"],"readme":"\u003c!-- prettier-ignore --\u003e\n\u003cdiv align=\"center\"\u003e\n\n# ngx-vest-forms\n\nA lightweight, type-safe adapter between Angular template-driven forms and [Vest.js](https://vestjs.dev) validation. Build complex forms with unidirectional data flow, sophisticated async validations, and minimal boilerplate.\n\n[![npm version](https://img.shields.io/npm/v/ngx-vest-forms.svg?style=flat-square)](https://www.npmjs.com/package/ngx-vest-forms)\n[![Build Status](https://img.shields.io/github/actions/workflow/status/ngx-vest-forms/ngx-vest-forms/cd.yml?branch=master\u0026style=flat-square\u0026label=Build)](https://github.com/ngx-vest-forms/ngx-vest-forms/actions/workflows/cd.yml)\n[![Angular](\u003chttps://img.shields.io/badge/Angular-19+%20(min)%20%E2%80%94%2020%20recommended-dd0031?style=flat-square\u0026logo=angular\u003e)](https://angular.dev)\n[![TypeScript](https://img.shields.io/badge/TypeScript-blue?style=flat-square\u0026logo=typescript\u0026logoColor=white)](https://www.typescriptlang.org)\n[![License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)](LICENSE)\n\n⭐ If you like this project, star it on GitHub — it helps a lot!\n\n[Quick Start](#installation--quick-start) • [Docs](#documentation) • [Key Features](#key-features) • [Migration](#migration) • [FAQ](#faq) • [Resources](#resources)\n\n\u003c/div\u003e\n\n\u003e **New Maintainer**:\n\u003e\n\u003e I'm [the-ult](https://bsky.app/profile/the-ult.bsky.social), now maintaining this project as Brecht Billiet has moved on to other priorities. Huge thanks to Brecht for creating this amazing library and his foundational work on Angular forms!\n\n## Why ngx-vest-forms?\n\n- Unidirectional state with Angular signals\n- Type-safe template-driven forms with runtime shape validation (dev only)\n- Powerful Vest.js validations (sync/async, conditional, composable)\n- Minimal boilerplate: controls and validation wiring are automatic\n\nSee the full guides under [Documentation](#documentation).\n\n## Installation \u0026 Quick Start\n\n### Prerequisites\n\n- **Angular**: \u003e=19.0.0 minimum, 20.x recommended (all used APIs stable)\n- **Vest.js**: \u003e=5.4.6 (Validation engine)\n- **TypeScript**: \u003e=5.8.0 (Modern Angular features)\n- **Node.js**: \u003e=20 (Maintenance release)\n\n### Installation\n\n```bash\nnpm install ngx-vest-forms\n```\n\n\u003e **v.2.0.0 NOTE:**\n\u003e\n\u003e You must call `only()` **unconditionally** in Vest suites.\n\u003e\n\u003e ```ts\n\u003e // ✅ Correct\n\u003e only(field); // only(undefined) safely runs all tests\n\u003e ```\n\u003e\n\u003e Why: Conditional `only()` breaks Vest's change detection mechanism and causes timing issues with `omitWhen` + `validationConfig` in ngx-vest-forms.\n\u003e See the [Migration Guide](./docs/migration/MIGRATION-v1.x-to-v2.0.0.md#1-unconditional-only-pattern-required-critical).\n\u003e\n\u003e Selector prefix: use `ngx-` (recommended). The legacy `sc-` works in v2.x but is deprecated and will be removed in v3.\n\n### Quick Start\n\nStart simple (with validations):\n\n```ts\nimport { Component, signal } from '@angular/core';\nimport { NgxVestForms, NgxDeepPartial, NgxVestSuite } from 'ngx-vest-forms';\nimport { staticSuite, only, test, enforce } from 'vest';\n\ntype MyFormModel = NgxDeepPartial\u003c{ email: string; name: string }\u003e;\n\n// Minimal validation suite (always call only(field) unconditionally)\nconst suite: NgxVestSuite\u003cMyFormModel\u003e = staticSuite((model, field?) =\u003e {\n  only(field);\n  test('email', 'Email is required', () =\u003e {\n    enforce(model.email).isNotBlank();\n  });\n});\n\n@Component({\n  imports: [NgxVestForms],\n  template: `\n    \u003cform ngxVestForm [suite]=\"suite\" (formValueChange)=\"formValue.set($event)\"\u003e\n      \u003cngx-control-wrapper\u003e\n        \u003clabel for=\"email\"\u003eEmail\u003c/label\u003e\n        \u003cinput id=\"email\" name=\"email\" [ngModel]=\"formValue().email\" /\u003e\n        \u003c!-- Errors display automatically below input --\u003e\n      \u003c/ngx-control-wrapper\u003e\n\n      \u003cngx-control-wrapper\u003e\n        \u003clabel for=\"name\"\u003eName\u003c/label\u003e\n        \u003cinput id=\"name\" name=\"name\" [ngModel]=\"formValue().name\" /\u003e\n      \u003c/ngx-control-wrapper\u003e\n\n      \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n    \u003c/form\u003e\n  `,\n})\nexport class MyComponent {\n  protected readonly formValue = signal\u003cMyFormModel\u003e({});\n  protected readonly suite = suite;\n}\n```\n\nNotes.\n\n- Use `[ngModel]` (not `[(ngModel)]`) for unidirectional data flow\n- The `?` operator is required because template-driven forms build values incrementally (`NgxDeepPartial`)\n- The `name` attribute MUST exactly match the property path used in `[ngModel]` — see [Field Paths](./docs/FIELD-PATHS.md)\n\nThat's all you need. The directive automatically creates controls, wires validation, and manages state.\n\n## Key Features\n\n- **Unidirectional state with signals** — Models are `NgxDeepPartial\u003cT\u003e` so values build up incrementally\n- **Type-safe with runtime shape validation** — Automatic control creation and validation wiring (dev mode checks)\n- **Vest.js validations** — Sync/async, conditional, composable patterns with `only(field)` optimization\n- **Error display modes** — Control when errors show: `on-blur`, `on-submit`, `on-blur-or-submit` (default), `on-dirty`, or `always`\n- **Warning display modes** — Control when warnings show: `on-touch`, `on-validated-or-touch` (default), `on-dirty`, or `always`\n- **Form state tracking** — Access touched, dirty, valid/invalid states for individual fields or entire form\n- **Error display helpers** — `ngx-control-wrapper` component (recommended) plus directive building blocks for custom wrappers:\n  - `ngx-form-group-wrapper` component (recommended for `ngModelGroup` containers)\n  - `FormErrorDisplayDirective` (state + display policy)\n  - `FormErrorControlDirective` (adds ARIA wiring + stable region IDs)\n- **Cross-field dependencies** — `validationConfig` for field-to-field triggers, `ROOT_FORM` for form-level rules\n- **Utilities** — Field paths, field clearing, validation config builder\n\n### Compatibility \u0026 Safety Notes (v2.x)\n\n- `ROOT_FORM_CONSTANT` is retained for compatibility but deprecated; prefer `ROOT_FORM`.\n- `set` / `cloneDeep` are retained for compatibility; prefer `setValueAtPath` / `structuredClone` in new code.\n\n### Error \u0026 Warning Display Modes\n\nControl when validation errors and warnings are shown to users with multiple built-in modes:\n\n#### Error Display Modes\n\n```typescript\n// Global configuration via DI token\nimport { NGX_ERROR_DISPLAY_MODE_TOKEN } from 'ngx-vest-forms';\n\nproviders: [\n  { provide: NGX_ERROR_DISPLAY_MODE_TOKEN, useValue: 'on-dirty' }\n]\n\n// Recommended: Use ngx-control-wrapper component\n\u003cngx-control-wrapper [errorDisplayMode]=\"'on-blur'\"\u003e\n  \u003cinput name=\"email\" [ngModel]=\"formValue().email\" /\u003e\n\u003c/ngx-control-wrapper\u003e\n```\n\n| Mode                  | Behavior                                             |\n| --------------------- | ---------------------------------------------------- |\n| `'on-blur-or-submit'` | Show after blur OR form submit (default)             |\n| `'on-blur'`           | Show only after blur/touch                           |\n| `'on-submit'`         | Show only after form submission                      |\n| `'on-dirty'`          | Show as soon as value changes (or after blur/submit) |\n| `'always'`            | Show immediately, even on pristine fields            |\n\n#### Warning Display Modes\n\n```typescript\n// Global configuration via DI token\nimport { NGX_WARNING_DISPLAY_MODE_TOKEN } from 'ngx-vest-forms';\n\nproviders: [\n  { provide: NGX_WARNING_DISPLAY_MODE_TOKEN, useValue: 'always' }\n]\n\n// Per-instance configuration\n\u003cngx-control-wrapper [warningDisplayMode]=\"'on-dirty'\"\u003e\n  \u003cinput name=\"username\" [ngModel]=\"formValue().username\" /\u003e\n\u003c/ngx-control-wrapper\u003e\n```\n\n| Mode                      | Behavior                                             |\n| ------------------------- | ---------------------------------------------------- |\n| `'on-validated-or-touch'` | Show after validation runs or touch (default)        |\n| `'on-touch'`              | Show only after blur/touch                           |\n| `'on-dirty'`              | Show as soon as value changes (or after blur/submit) |\n| `'always'`                | Show immediately, even on pristine fields            |\n\n#### Group-Safe Mode Example\n\n```html\n// Group-safe mode (use this on an ngModelGroup container)\n\u003cngx-form-group-wrapper ngModelGroup=\"address\"\u003e\n  \u003cngx-control-wrapper\u003e\n    \u003clabel for=\"street\"\u003eStreet\u003c/label\u003e\n    \u003cinput id=\"street\" name=\"street\" [ngModel]=\"formValue().address?.street\" /\u003e\n  \u003c/ngx-control-wrapper\u003e\n\n  \u003cngx-control-wrapper\u003e\n    \u003clabel for=\"city\"\u003eCity\u003c/label\u003e\n    \u003cinput id=\"city\" name=\"city\" [ngModel]=\"formValue().address?.city\" /\u003e\n  \u003c/ngx-control-wrapper\u003e\n\u003c/ngx-form-group-wrapper\u003e\n```\n\n#### ARIA association (advanced)\n\n`\u003cngx-control-wrapper\u003e` can optionally apply `aria-describedby` / `aria-invalid` to **descendant** controls.\nThis is controlled by `ariaAssociationMode`:\n\n- `\"all-controls\"` (default) — stamps all descendant `input/select/textarea`\n- `\"single-control\"` — stamps only if exactly one control exists (useful for input + extra buttons)\n- `\"none\"` — never mutates descendant controls (group-safe / manual wiring)\n\nFor `ngModelGroup` containers, prefer using `\u003cngx-form-group-wrapper\u003e` (group-safe by default).\n\n📖 See also:\n\n- [Accessibility Guide](./docs/ACCESSIBILITY.md)\n- [`ControlWrapperComponent` docs](./projects/ngx-vest-forms/src/lib/components/control-wrapper/README.md)\n\n\u003e **Styling note**: `ngx-control-wrapper` uses Tailwind CSS utility classes for default styling.\n\u003e If your project doesn't use Tailwind, see the [component docs](./projects/ngx-vest-forms/src/lib/components/control-wrapper/README.md#styling-dependency-tailwind-css) for alternatives.\n\n📖 **[Complete Guide: Custom Control Wrappers](./docs/CUSTOM-CONTROL-WRAPPERS.md)**\n\n### Form State\n\nAccess complete form and field state through the `FormErrorDisplayDirective` or `FormControlStateDirective`:\n\n```typescript\n@Component({\n  template: `\n    \u003cngx-control-wrapper #wrapper=\"ngxErrorDisplay\"\u003e\n      \u003cinput name=\"email\" [ngModel]=\"formValue().email\" /\u003e\n\n      @if (wrapper.isTouched()) {\n        \u003cspan\u003eField was touched\u003c/span\u003e\n      }\n      @if (wrapper.isPending()) {\n        \u003cspan\u003eValidating...\u003c/span\u003e\n      }\n    \u003c/ngx-control-wrapper\u003e\n  `\n})\n```\n\n**Available state signals:**\n\n- `isTouched()` / `isDirty()` — User interaction state\n- `isValid()` / `isInvalid()` — Validation state\n- `isPending()` — Async validation in progress\n- `errorMessages()` / `warningMessages()` — Current validation messages\n- `shouldShowErrors()` / `shouldShowWarnings()` — Computed based on display mode and state\n\n**Warnings behavior:**\n\n- Warnings are **non-blocking** and do not make a field invalid.\n- They are stored separately from `control.errors` and are cleared on `resetForm()`.\n- These messages may appear after `validationConfig` triggers validation, even if the field was not touched yet.\n- Use `NGX_WARNING_DISPLAY_MODE_TOKEN` to control when warnings display (see [Warning Display Modes](#warning-display-modes)).\n\n**Tip**: For async validations, use `createDebouncedPendingState()` to prevent \"Validating...\" messages from flashing when validation completes quickly (\u003c 200ms).\n\n📖 **[Complete Guide: Custom Control Wrappers](./docs/CUSTOM-CONTROL-WRAPPERS.md)**\n\n## Advanced Features\n\n### Validation Config\n\nAutomatically re-validate dependent fields when another field changes. Essential when using Vest.js's `omitWhen`/`skipWhen` for conditional validations.\n\n**When to use**: Password confirmation, conditional required fields, or any field that depends on another field's value.\n\n```typescript\nprotected readonly validationConfig = {\n  'password': ['confirmPassword'],  // When password changes, re-validate confirmPassword\n  'age': ['emergencyContact']       // When age changes, re-validate emergencyContact\n};\n```\n\n**Important**: `validationConfig` only triggers re-validation—validation logic is always defined in your Vest suite.\n\n📖 **[Complete Guide: ValidationConfig vs Root-Form](./docs/VALIDATION-CONFIG-VS-ROOT-FORM.md)**\n\n### Root-Form Validation\n\nForm-level validation rules that don't belong to any specific field (e.g., \"at least one contact method required\").\n\n**When to use**: Business rules that evaluate multiple fields but errors should appear at form level, not on individual fields.\n\n```typescript\nimport { ROOT_FORM } from 'ngx-vest-forms';\n\n// In your Vest suite\ntest(ROOT_FORM, 'At least one contact method is required', () =\u003e {\n  enforce(model.email || model.phone).isTruthy();\n});\n```\n\n```html\n\u003c!-- In template --\u003e\n\u003cform ngxVestForm ngxValidateRootForm [suite]=\"suite\"\u003e\n  \u003c!-- Show form-level errors --\u003e\n  \u003cdiv *ngIf=\"vestForm.errors?.rootForm\"\u003e{{ vestForm.errors.rootForm }}\u003c/div\u003e\n\u003c/form\u003e\n```\n\n📖 **[Complete Guide: ValidationConfig vs Root-Form](./docs/VALIDATION-CONFIG-VS-ROOT-FORM.md)**\n\n### Dynamic Form Structure\n\nManually trigger validation when form structure changes between **input fields and non-input content** (like `\u003cp\u003e` tags) without value changes.\n\n**When to use**: When switching from form controls to informational text/paragraphs where no control values change.\n\n**NOT needed when**: Switching between different input fields (value changes trigger validation automatically).\n\n**IMPORTANT**: `triggerFormValidation()` only re-runs validation logic—it does NOT mark fields as touched or show errors.\n\n\u003e **Note on form submission**: With the default `on-blur-or-submit` error display mode, errors are shown automatically when you submit via `(ngSubmit)`. The form automatically calls `markAllAsTouched()` internally. You only need to call `markAllAsTouched()` manually for special cases like multiple forms with one submit button.\n\n```typescript\n// Structure change: Re-run validation\n@if (type() === 'typeA') {\n  \u003cinput name=\"fieldA\" [ngModel]=\"formValue().fieldA\" /\u003e\n} @else {\n  \u003cp\u003eNo input required\u003c/p\u003e  // ← No form control, needs triggerFormValidation()\n}\n\nonTypeChange(newType: string) {\n  this.formValue.update(v =\u003e ({ ...v, type: newType }));\n  this.vestForm.triggerFormValidation();  // Re-runs validation, doesn't show errors\n}\n\n// Standard form submission - NO manual call needed!\n// Errors shown automatically via (ngSubmit) with default on-blur-or-submit mode\n\u003cform ngxVestForm (ngSubmit)=\"save()\"\u003e\n  \u003c!-- ... --\u003e\n  \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n\u003c/form\u003e\n\n// Multiple forms with one button - NEED manual markAllAsTouched()\nsubmitBoth() {\n  this.form1().markAllAsTouched();\n  this.form2().markAllAsTouched();\n  if (this.form1().valid \u0026\u0026 this.form2().valid) {\n    // Submit logic\n  }\n}\n```\n\n📖 **[Complete Guide: Structure Change Detection](./docs/STRUCTURE_CHANGE_DETECTION.md)**\n\n### Shape Validation (Development Mode)\n\nIn development mode, ngx-vest-forms validates that your form's structure matches your TypeScript model, catching common mistakes early:\n\n```typescript\n// Your model\ntype MyFormModel = NgxDeepPartial\u003c{\n  email: string;\n  address: { street: string; city: string };\n}\u003e;\n\n// Define shape for runtime validation\nconst shape: NgxDeepRequired\u003cMyFormModel\u003e = {\n  email: '',\n  address: { street: '', city: '' },\n};\n```\n\n```html\n\u003cform ngxVestForm [suite]=\"suite\" [formShape]=\"shape\"\u003e\n  \u003c!-- ✅ Correct: matches shape --\u003e\n  \u003cinput name=\"email\" [ngModel]=\"formValue().email\" /\u003e\n  \u003cinput name=\"address.street\" [ngModel]=\"formValue().address?.street\" /\u003e\n\n  \u003c!-- ❌ Error in dev mode: typo detected --\u003e\n  \u003cinput name=\"emial\" [ngModel]=\"formValue().email\" /\u003e\n\n  \u003c!-- ❌ Error in dev mode: path doesn't exist in shape --\u003e\n  \u003cinput name=\"address.zipcode\" [ngModel]=\"formValue().address?.zipcode\" /\u003e\n\u003c/form\u003e\n```\n\n**Benefits:**\n\n- Catch typos in `name` attributes immediately during development\n- Ensure template structure matches TypeScript model\n- Zero runtime cost in production (checks disabled automatically)\n- Works with nested objects and arrays\n\n**Important**: Shape validation only runs in development mode (`isDevMode()` returns `true`). Production builds have zero overhead.\n\n📖 **[Complete Guide: Field Paths](./docs/FIELD-PATHS.md)**\n\n## Documentation\n\n### Getting Started\n\n- **[Complete Example](./docs/COMPLETE-EXAMPLE.md)** - Step-by-step walkthrough from basic form to advanced patterns\n- **[Composable Validations](./docs/COMPOSABLE-VALIDATIONS.md)** - Break validation logic into reusable, testable functions\n\n### Advanced Patterns\n\n- **[ValidationConfig vs Root-Form](./docs/VALIDATION-CONFIG-VS-ROOT-FORM.md)** - Cross-field dependencies and form-level rules\n- **[Clear Submitted State](./docs/CLEAR-SUBMITTED-STATE.md)** - End a submit cycle without resetting values or control metadata\n- **[Field Path Types](./docs/FIELD-PATHS.md)** - Type-safe dot-notation paths for nested properties\n- **[Structure Change Detection](./docs/STRUCTURE_CHANGE_DETECTION.md)** - Handle dynamic form structure updates\n- **[Field Clearing Utilities](./docs/FIELD-CLEARING-UTILITIES.md)** - Type-safe utilities for clearing nested form values\n\n### UI \u0026 Integration\n\n- **[Child Components](./docs/CHILD-COMPONENTS.md)** - Split large forms into smaller, maintainable components\n- **[Custom Control Wrappers](./docs/CUSTOM-CONTROL-WRAPPERS.md)** - Build consistent error display patterns\n- **[API Tokens](./docs/API-TOKENS.md)** - Configure error display modes and other global settings\n\n### Reference\n\n- **[Utilities README](./projects/ngx-vest-forms/src/lib/utils/README.md)** - Canonical reference for all utility functions\n\n### Examples\n\n- **[Examples Project](./projects/examples)** - Working code examples with business hours forms, purchase forms, and validation config demos\n  - Run locally: `npm install \u0026\u0026 npm start`\n  - Includes smart components, UI components, and complete validation patterns\n\n## Migration\n\n- v1.x → v2.0.0: **[Migration Guide](./docs/migration/MIGRATION-v1.x-to-v2.0.0.md)**\n- Selector prefixes: **[Dual Selector Support](./docs/DUAL-SELECTOR-SUPPORT.md)**\n\nBrowser support follows Angular 19+ targets (no `structuredClone` polyfill required).\n\n## FAQ\n\n### Do I need validations to use ngx-vest-forms?\n\nNo—but you’ll almost always want them. Common cases to start without a suite:\n\n- Prototyping UI while deferring rules\n- Gradual migration: adopt unidirectional state and type-safe models first\n- Server-driven validation: display backend errors while you add a client suite later\n\nYou can add a Vest suite at any time by binding `[suite]` on the form.\n\n## Resources\n\n### Documentation \u0026 Tutorials\n\n- **[Angular Official Documentation](https://angular.dev/guide/forms)** - Template-driven forms guide\n- **[Vest.js Documentation](https://vestjs.dev)** - Validation framework used by ngx-vest-forms\n- **[Live Examples Repository](https://github.com/ngx-vest-forms/ngx-vest-forms/tree/master/projects/examples)** - Complex form examples and patterns\n\n### Running Examples Locally\n\n```bash\nnpm install\nnpm start\n```\n\n### Learning Resources\n\n**[Complex Angular Template-Driven Forms Course](https://www.simplified.courses/complex-angular-template-driven-forms)** - Master advanced form patterns and become a form expert.\n\n### Founding Articles by Brecht Billiet\n\nThis library was originally created by [Brecht Billiet](https://twitter.com/brechtbilliet). Here are his foundational blog posts that inspired and guided the development:\n\n- **[Introducing ngx-vest-forms](https://blog.simplified.courses/introducing-ngx-vest-forms/)** - The original introduction and motivation\n- **[Making Angular Template-Driven Forms Type-Safe](https://blog.simplified.courses/making-angular-template-driven-forms-typesafe/)** - Deep dive into type safety\n- **[Asynchronous Form Validators in Angular with Vest](https://blog.simplified.courses/asynchronous-form-validators-in-angular-with-vest/)** - Advanced async validation patterns\n- **[Template-Driven Forms with Form Arrays](https://blog.simplified.courses/template-driven-forms-with-form-arrays/)** - Dynamic form arrays implementation\n\n## Developer Resources\n\n### Agent Skills\n\nThis repository ships an installable agent skill for ngx-vest-forms guidance.\n\nInstall it from the repository root with the `skills` CLI:\n\n```bash\nnpx skills add ngx-vest-forms/ngx-vest-forms --skill ngx-vest-forms\n```\n\nThis repository also ships an installable agent skill for **Vest.js 5.4 guidance**.\n\nInstall it from the repository root with the `skills` CLI:\n\n```bash\nnpx skills add ngx-vest-forms/ngx-vest-forms --skill vestjs\n```\n\nSee also: **[Vest.js 5.4 Agent Skill Guide](./docs/VESTJS-SKILL.md)**\n\n### Comprehensive Instruction Files\n\nThis project includes detailed instruction files designed to help developers master ngx-vest-forms and Vest.js patterns:\n\n- **[`.github/instructions/ngx-vest-forms.instructions.md`](.github/instructions/ngx-vest-forms.instructions.md)** - Complete guide for using ngx-vest-forms library\n- **[`.github/instructions/vest.instructions.md`](.github/instructions/vest.instructions.md)** - Comprehensive Vest.js validation patterns and best practices\n- **[`.github/copilot-instructions.md`](.github/copilot-instructions.md)** - Main GitHub Copilot instructions for this workspace\n\n## Acknowledgments\n\n🙏 **Special thanks to [Brecht Billiet](https://twitter.com/brechtbilliet)** for creating the original version of this library and his pioneering work on Angular forms. His vision and expertise laid the foundation for what ngx-vest-forms has become today.\n\n### Core Contributors \u0026 Inspirations\n\n**[Evyatar Alush](https://twitter.com/evyataral)** - Creator of [Vest.js](https://vestjs.dev/)\n\n- 🎯 **The validation engine** that powers ngx-vest-forms\n- 🎙️ **Featured on PodRocket**: [Vest with Evyatar Alush](https://dev.to/podrocket/vest-with-evyatar-alush) - Deep dive into the philosophy and architecture of Vest.js\n\n**[Ward Bell](https://twitter.com/wardbell)** - Template-Driven Forms Advocate\n\n- 📢 **Evangelized Template-Driven Forms**: [Prefer Template-Driven Forms](https://devconf.net/talk/prefer-template-driven-forms-ward-bell-ng-conf-2021) (ng-conf 2021)\n- 🎥 **Original Vest.js + Angular Integration**: [Form validation done right](https://www.youtube.com/watch?v=EMUAtQlh9Ko) - The foundational talk that inspired this approach\n- 💻 **Early Implementation**: [ngc-validate](https://github.com/wardbell/ngc-validate) - The initial version of template-driven forms with Vest.js\n\nThese pioneers laid the groundwork that made ngx-vest-forms possible, combining the power of declarative validation with the elegance of Angular's template-driven approach.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fngx-vest-forms%2Fngx-vest-forms","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fngx-vest-forms%2Fngx-vest-forms","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fngx-vest-forms%2Fngx-vest-forms/lists"}