{"id":22945132,"url":"https://github.com/seregpie/ngx-advanced-forms","last_synced_at":"2026-05-17T00:41:49.823Z","repository":{"id":65367743,"uuid":"547282735","full_name":"SeregPie/ngx-advanced-forms","owner":"SeregPie","description":"Everything to make your work with Angular forms easier.","archived":false,"fork":false,"pushed_at":"2024-04-06T08:06:07.000Z","size":154,"stargazers_count":4,"open_issues_count":5,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-11-16T18:48:02.414Z","etag":null,"topics":["angular","array","bridge","control","form","record","service","util","validation"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SeregPie.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2022-10-07T12:32:44.000Z","updated_at":"2024-05-01T02:03:27.000Z","dependencies_parsed_at":"2023-09-27T13:45:57.288Z","dependency_job_id":"8f433e90-9118-4b12-a9b3-1b049fe4a724","html_url":"https://github.com/SeregPie/ngx-advanced-forms","commit_stats":{"total_commits":59,"total_committers":1,"mean_commits":59.0,"dds":0.0,"last_synced_commit":"9b0e0faf5b97b4f072e1c606477b8bb7a6b50ab0"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SeregPie%2Fngx-advanced-forms","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SeregPie%2Fngx-advanced-forms/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SeregPie%2Fngx-advanced-forms/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SeregPie%2Fngx-advanced-forms/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SeregPie","download_url":"https://codeload.github.com/SeregPie/ngx-advanced-forms/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":229716010,"owners_count":18113024,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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","array","bridge","control","form","record","service","util","validation"],"created_at":"2024-12-14T14:29:49.303Z","updated_at":"2026-05-17T00:41:49.775Z","avatar_url":"https://github.com/SeregPie.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NgxAdvancedForms\n\nEverything to make your work with Angular forms easier.\n\n## Setup\n\n```sh\nnpm i ngx-advanced-forms\n```\n\n---\n\n```ts\nimport {\n  DynamicFormArray,\n  withCustomValidator,\n  /* ... */\n} from 'ngx-advanced-forms';\n```\n\n## API\n\n- **`C`** [DynamicFormArray](#dynamicformarray)\n- **`C`** [DynamicFormRecord](#dynamicformrecord)\n- **`C`** [FormControlService](#formcontrolservice)\n- **`C`** [FallthroughFormService](#fallthroughformservice)\n- **`T`** [CustomValidatorFn](#customvalidatorfn)\n- **`T`** [CustomAsyncValidatorFn](#customasyncvalidatorfn)\n- **`F`** [withCustomValidator](#withcustomvalidator)\n- **`F`** [withCustomAsyncValidator](#withcustomasyncvalidator)\n- **`F`** [composeValidators](#composevalidators)\n- **`T`** [ControlStateAccessor](#controlstateaccessor)\n- **`T`** [CreateControlStateAccessorFn](#createcontrolstateaccessorfn)\n- **`F`** [updateFormState](#updateformstate)\n\n### DynamicFormArray\n\nA sup-class of `FormArray` that creates or removes sub-controls dynamically based on the passed value.\n\n#### Type\n\n```ts\nclass DynamicFormArray\u003cTControl\u003e extends FormArray\u003cTControl\u003e {\n  constructor(controlFactory: () =\u003e TControl, options?: AbstractControlOptions);\n}\n```\n\n### DynamicFormRecord\n\nA sup-class of `FormRecord` that creates or removes sub-controls dynamically based on the passed value.\n\n#### Type\n\n```ts\nclass DynamicFormRecord\u003cTControl\u003e extends FormRecord\u003cTControl\u003e {\n  constructor(controlFactory: () =\u003e TControl, options?: AbstractControlOptions);\n}\n```\n\n### FormControlService\n\nImplements all necessary tools to connect to the overlying control.\n\n#### Type\n\n```ts\n@Injectable()\nclass FormControlService\u003cTValue\u003e {\n  static provide(): Provider;\n\n  get value(): null | TValue;\n  set value(v: null | TValue);\n\n  readonly valueChanges: Observable\u003cnull | TValue\u003e;\n\n  get disabled(): boolean;\n\n  readonly disabledChanges: Observable\u003cboolean\u003e;\n\n  get errors(): null | ValidationErrors;\n  set errors(v: null | ValidationErrors);\n\n  readonly errorsChanges: Observable\u003cnull | ValidationErrors\u003e;\n\n  get pending(): boolean;\n  set pending(v: boolean);\n\n  readonly pendingChanges: Observable\u003cboolean\u003e;\n\n  touch(): void;\n\n  readonly touchEvents: Observable\u003cvoid\u003e;\n}\n```\n\n### FallthroughFormService\n\nPasses a control from a control directive through.\n\n#### Type\n\n```ts\n@Injectable()\nclass FallthroughFormService {\n  static provide(): Provider;\n\n  readonly controlDirective: null | AbstractControlDirective;\n\n  readonly control: null | AbstractControl;\n}\n```\n\n#### Details\n\n- Works with any reactive and non-reactive control directive.\n- The control is available after the component is initialized.\n\n#### Usage\n\n```ts\n@Component({\n  imports: [ReactiveFormsModule, MyNumberInputComponent],\n  providers: [FallthroughFormService.provide()],\n  standalone: true,\n  template: `\n    \u003cmy-number-input\n      [formControl]=\"form\"\n      [label]=\"label\"\n      [max]=\"100\"\n      [min]=\"0\"\n      [step]=\"0.1\"\n      unit=\"%\"\n    /\u003e\n  `,\n  selector: 'my-percent-input',\n})\nclass MyPercentInputComponent {\n  constructor(public fallthroughFormService: FallthroughFormService) {}\n\n  get form() {\n    return this.fallthroughFormService.control as FormControl;\n  }\n\n  @Input()\n  label: string = '';\n}\n```\n\n### CustomValidatorFn\n\n#### Type\n\n```ts\ninterface CustomValidatorFn\u003cTControl\u003e {\n  (control: TControl): null | ValidationErrors;\n}\n```\n\n### CustomAsyncValidatorFn\n\n#### Type\n\n```ts\ninterface CustomAsyncValidatorFn\u003cTControl\u003e {\n  (control: TControl): Promise\u003cnull | ValidationErrors\u003e | Observable\u003cnull | ValidationErrors\u003e;\n}\n```\n\n### withCustomValidator\n\nAdds a typed validator to a control.\n\n#### Type\n\n```ts\nfunction withCustomValidator\u003cTControl\u003e(\n  control: TControl,\n  validator: CustomValidatorFn\u003cTControl\u003e,\n): TControl;\n```\n\n#### Details\n\n- Recalculates the validation status of the control.\n\n#### Usage\n\n```ts\nconst form = new FormGroup({\n  email: new FormControl\u003cstring\u003e('', {\n    validators: [Validators.required, Validators.email],\n  }),\n  password: withCustomValidator(\n    new FormGroup({\n      actual: new FormControl\u003cstring\u003e('', {\n        validators: [Validators.required, Validators.minLength(8)],\n      }),\n      verify: new FormControl\u003cstring\u003e(''),\n    }),\n    (form) =\u003e {\n      if (form.controls.actual.valid) {\n        if (form.controls.actual.value !== form.controls.verify.value) {\n          return {verifyPassword: true};\n        }\n      }\n      return null;\n    },\n  ),\n});\n```\n\n### withCustomAsyncValidator\n\nAdds a typed asynchronous validator to a control.\n\n#### Type\n\n```ts\nfunction withCustomAsyncValidator\u003cTControl\u003e(\n  control: TControl,\n  validator: CustomAsyncValidatorFn\u003cTControl\u003e,\n): TControl;\n```\n\n#### Details\n\n- Behaves the same as `withCustomValidator`.\n\n### composeValidators\n\nComposes multiple validators into one.\n\n#### Type\n\n```ts\nfunction composeValidators\u003cTControl\u003e(\n  validators: Array\u003cCustomValidatorFn\u003cTControl\u003e\u003e,\n): CustomValidatorFn\u003cTControl\u003e;\n```\n\n#### Usage\n\n```ts\nconst form = new FormControl\u003cnull | number\u003e(null, {\n  validators: composeValidators([\n    Validators.required,\n    Validators.min(0),\n    Validators.max(100),\n  ]),\n});\n```\n\n### ControlStateAccessor\n\n#### Type\n\n```ts\ninterface ControlStateAccessor\u003cTControl\u003e {\n  readonly control: TControl;\n\n  get disabled(): boolean;\n  set disabled(v: boolean);\n\n  get enabled(): boolean;\n  set enabled(v: boolean);\n}\n```\n\n### CreateControlStateAccessorFn\n\n#### Type\n\n```ts\ninterface CreateControlStateAccessorFn {\n  \u003cTControl\u003e(control: TControl): ControlStateAccessor\u003cTControl\u003e;\n}\n```\n\n### updateFormState\n\nProvides a convenient way to manage the enabled/disabled state of multiple nested controls.\n\n#### Type\n\n```ts\nfunction updateFormState\u003cTControl\u003e(\n  control: TControl,\n  fn: {(wrap: CreateControlStateAccessorFn): void},\n): void;\n```\n\n#### Details\n\n- Accepts only the provided control and its descendants.\n- The order of the statements doesn't matter.\n- Prevents unnecessary events from being emitted when no changes are detected.\n\n#### Usage\n\n```ts\nclass {\n  form = new FormGroup({\n    unit: new FormControl\u003c'meter' | 'feet'\u003e('meter'),\n    valueInMeters: new FormControl\u003cnull | number\u003e(null),\n    valueInFeet: new FormControl\u003cnull | number\u003e(null),\n  });\n\n  ngAfterContentChecked(): void {\n    const {form} = this;\n    updateFormState(form, (wrap) =\u003e {\n      const {unit} = form.getRawValue();\n      wrap(form.controls.valueInMeters).enabled = unit === 'meter';\n      wrap(form.controls.valueInFeet).enabled = unit === 'feet';\n    });\n  }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseregpie%2Fngx-advanced-forms","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseregpie%2Fngx-advanced-forms","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseregpie%2Fngx-advanced-forms/lists"}