{"id":17605573,"url":"https://github.com/paddls/ngx-form","last_synced_at":"2025-03-09T03:31:36.034Z","repository":{"id":45562326,"uuid":"318574410","full_name":"paddls/ngx-form","owner":"paddls","description":"Model based typed reactive forms made easy.","archived":false,"fork":false,"pushed_at":"2024-10-21T09:34:16.000Z","size":1537,"stargazers_count":14,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-30T09:00:23.491Z","etag":null,"topics":["angular","forms","strongly-typed","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@paddls/ngx-form","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/paddls.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,"publiccode":null,"codemeta":null}},"created_at":"2020-12-04T16:33:01.000Z","updated_at":"2024-10-21T09:28:37.000Z","dependencies_parsed_at":"2024-11-08T09:38:33.297Z","dependency_job_id":null,"html_url":"https://github.com/paddls/ngx-form","commit_stats":{"total_commits":95,"total_committers":6,"mean_commits":"15.833333333333334","dds":0.6105263157894737,"last_synced_commit":"8b8113ef039bce9e2d10bcc00a2b9f3bf02921c7"},"previous_names":[],"tags_count":35,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paddls%2Fngx-form","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paddls%2Fngx-form/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paddls%2Fngx-form/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paddls%2Fngx-form/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/paddls","download_url":"https://codeload.github.com/paddls/ngx-form/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240564599,"owners_count":19821422,"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","forms","strongly-typed","typescript"],"created_at":"2024-10-22T15:01:15.833Z","updated_at":"2025-03-09T03:31:35.997Z","avatar_url":"https://github.com/paddls.png","language":"TypeScript","funding_links":[],"categories":["Third Party Components"],"sub_categories":["Forms"],"readme":"# NGX-FORM\n\n![ngx-form-ci](https://github.com/paddls/ngx-form/workflows/build/badge.svg?branch=master)\n[![Coverage Status](https://coveralls.io/repos/github/paddls/ngx-form/badge.svg?branch=master)](https://coveralls.io/github/paddls/ngx-form?branch=master)\n[![npm version](https://badge.fury.io/js/%40paddls%2Fngx-form.svg)](https://badge.fury.io/js/%40paddls%2Fngx-form)\n![GitHub](https://img.shields.io/github/license/paddls/ngx-form)\n![GitHub repo size](https://img.shields.io/github/repo-size/paddls/ngx-form)\n![GitHub last commit](https://img.shields.io/github/last-commit/paddls/ngx-form)\n![GitHub issues](https://img.shields.io/github/issues/paddls/ngx-form)\n![GitHub top language](https://img.shields.io/github/languages/top/paddls/ngx-form)\n\nModel based typed reactive forms made easy.\n\n## Summary\n\n* [How to install](#how-to-install)\n* [Basic usage](#basic-usage)\n  * [Create a form](#create-a-form)\n  * [Build a form](#build-a-form)\n* [FormGroup](#formgroup)\n* [FormArray](#formarray)\n* [Validator, AsyncValidator and UpdateOn](#validator-asyncvalidator-and-updateon)\n  * [ValidatorFactory](#validatorfactory)\n  * [AsyncValidatorFactory](#asyncvalidatorfactory)\n* [FormChild](#formchild)\n* [Form lifecycle](#form-lifecycle)\n  * [getValue()](#getvalue--)\n  * [setValue()](#setvalue--)\n  * [patchValue()](#patchvalue--)\n  * [restore()](#restore--)\n  * [empty()](#empty--)\n  * [cancel()](#cancel--)\n  * [markAllAsDirty()](#markallasdirty--)\n  * [markAllAsPending()](#markallaspending--)\n  * [markAllAsPristine()](#markallaspristine--)\n  * [markAllAsUntouched()](#markallasuntouched--)\n  * [add()](#add--)\n* [DisableOn](#disableon)\n* [OnValueChanges]\n\n## How to install\n\nFirst install the library in your project :\n\n```shell script\nnpm install --save @paddls/ngx-form\n```\n\n### Recommended Angular versions\n\n| `Angular`          | `NgxForm`         |\n|--------------------|-------------------|\n| `19.0.0` and above | `9.0.0` and above |\n| `18.0.0` and above | `8.0.0` and above |\n| `17.0.0` and above | `7.0.0` and above |\n| `16.0.0` and above | `6.0.0` and above |\n| `15.0.0` and above | `5.0.0` and above |\n| `14.0.0` and above | `4.0.1` and above |\n| `13.0.0` and above | `3.0.1` and above |\n| `12.0.0` and above | `2.0.0` and above |\n| `11.0.0` and above | `1.0.0` and above |\n\nAfter that, import `NgxFormModule` as follows :\n\n```typescript\nimport { ReactiveFormsModule } from '@angular/forms';\nimport { NgxFormModule } from '@paddls/ngx-form';\n\n@NgModule({\n  imports: [\n    ReactiveFormsModule,\n    NgxFormModule\n  ]\n})\nexport class AppModule {\n}\n```\n\n## Basic usage\n\n### Create a form\n\nWith ngx-form, the form creation is model driven. Therefore, to create a form, you need to create a model class which\nwill represent the form.\n\nA form model should look like this :\n\n```typescript\nimport { FormControl } from '@paddls/ngx-form';\n\nexport class AddressForm {\n\n  @FormControl()\n  public city: string;\n\n  @FormControl({defaultValue: 3})\n  public streetNumber: number;\n\n  @FormControl({defaultValue: '10055', name: 'postalCode'})\n  public zipCode: string;\n\n  @FormControl('street')\n  public route: string;\n\n  public constructor(data: Partial\u003cAddressForm\u003e = {}) {\n    Object.assign(this, data);\n  }\n}\n```\n\nEach class attribute with the ``@FormControl()`` decorator will be marked as a form control. Inside\nthe ``@FormControl()`` annotation, you can add some context at your will. Two properties are\navailable : ``defaultValue`` and ``name``.\n\nBy specifying a ``defaultValue``, the form control will be initialized with this value at the form creation.\n\nWith the ``name`` property, you can differentiate the name given to the field in your model and the name of the control\nin the created form.\n\nYou can define only one of the properties in the context, or both. If you just want to specify the\n``name`` property, you can do it just by passing a string as the context like in the example above.\n\n### Build a form\n\nOnce you've created the model, you can build the form wherever you like through your entire Angular application using\nthe ``@BuildForm()`` decorator.\n\n```typescript\nimport { Component } from '@angular/core';\nimport { BuildForm, NgxFormGroup } from '@paddls/ngx-form';\nimport { AddressForm } from './form/address.form';\n\n@Component({\n  selector: 'app-root',\n  templateUrl: './app.component.html'\n})\nexport class AppComponent {\n\n  @BuildForm(() =\u003e AddressForm)\n  public addressForm: NgxFormGroup\u003cAddressForm\u003e;\n\n  public onSubmit(): void {\n    console.log(this.addressForm.getValue());\n  }\n}\n```\n\nThat's it ! You can now use your newly created form juste like any other reactive form. Furthermore, the form is\nstrongly typed. To retrieve the strongly typed result, just call ``form.getValue()`` method.\n\n## FormGroup\n\n```typescript\nimport { FormControl, FormGroup } from '@paddls/ngx-form';\nimport { AddressForm } from './address.form';\n\nexport class UserForm {\n\n  @FormControl({value: 'Brad'})\n  public firstName: string;\n\n  @FormControl({value: 'Pitt'})\n  public lastName: string;\n\n  @FormGroup(() =\u003e AddressForm)\n  public personalAddress: AddressForm;\n\n  @FormGroup({type: () =\u003e AddressForm, defaultValue: myAddressValue, name: 'companyAddress'})\n  public workAddress: AddressForm;\n}\n```\n\nTo nest forms, use the ``@FormGroup()`` decorator. Do not forget to specify the type of your child form in the decorator\ncontext.\n\nYou can also specify ``defaultValue`` and ``name`` properties if necessary. If you don't need to specify them, you can\nspecify the type in the context directly like in the example above.\n\n## FormArray\n\n```typescript\nimport { FormArray, FormControl } from '@paddls/ngx-form';\nimport { CompanyForm } from './company.form';\n\nexport class UserForm {\n\n  @FormControl({defaultValue: 'Leonardo'})\n  public firstName: string;\n\n  @FormControl()\n  public lastName: string;\n\n  @FormArray({defaultValue: 'Default skill', defaultValues: ['Java', 'C++'], updateOn: 'blur', name: 'userSkills'})\n  public skills: string[];\n\n  @FormArray(() =\u003e CompanyForm)\n  public companies: CompanyForm[];\n}\n```\n\nTo add a ``FormArray`` to your form model, just add an attribute with the ``@FormArray()``\ndecorator. Again, you can specify ``defaultValue`` and ``name`` properties if necessary. Like with the ``@FormGroup()``\ndecorator, you can specify a type if you wish to create an array of nested forms.\n\nAdditionally, you can add ``defaultValues`` or ``updateOn`` properties to the context.\n\nLike with the ``@FormControl()`` or ``@FormGroup()`` decorators, shorthands for ``name`` and ``type``\nproperties are available. Just specify the properties directly in the context if they are the only property used.\n\n## Validator, AsyncValidator and UpdateOn\n\n```typescript\nimport { AsyncValidator, FormControl, FormGroup, UpdateOn, Validator } from '@paddls/ngx-form';\nimport { AddressForm } from './address.form';\nimport { Validators } from '@angular/forms';\n\n@UpdateOn('change')\nexport class UserForm {\n\n  @FormControl({defaultValue: 'Thomas'})\n  @Validator(Validators.required)\n  @UpdateOn('blur')\n  public firstName: string;\n\n  @AsyncValidator(myAsyncValidator)\n  @FormControl('lastname')\n  public lastName: string;\n\n  @Validator([Validators.required, Validators.min(0)])\n  public age: number;\n\n  @FormGroup({type: () =\u003e AddressForm, defaultValue: structuredClone(defaultAddress)})\n  @UpdateOn('submit')\n  public personalAddress: AddressForm;\n}\n```\n\nTo add Validators, AsyncValidators or specify the ``updateOn`` property on any form, ``FormControl``,\n``FormGroup`` or ``FormArray``, just add a ``@Validator``, ``@AsyncValidator`` or ``@UpdateOn`` decorator on any form\nmodel attribute or on the form model class if you want to apply it to a form.\n\nThe ``@Validator`` or ``@AsyncValidator`` decorators take any ``ValidatorFn``, ``AsyncValidatorFn`` or arrays of\n``ValidatorFn`` and ``AsyncValidatorFn`` as parameters to apply them to the desired control or form. The\n``@UpdateOn`` decorator takes ``'change'``, ``'blur'`` or ``'submit'`` value as parameter to apply it to the desired\ncontrol or form.\n\n### ValidatorFactory\n\nYou can use a `ValidatorFactory` to build a `ValidatorFn` using values from any provider (`@Injectable()`) with the\nfollowing syntax :\n\n```typescript\nclass Form {\n\n  @Validator([\n    ValidatorFactory.of(((provider: MyProvider) =\u003e Validators.min(provider.length)), [MyProvider])\n  ])\n  @FormControl()\n  public secondaryAddress: string;\n}\n```\n\nYou can mix standard `ValidatorFn` and `ValidatorFactory` inside the `@Validator` decorator.\n\n### AsyncValidatorFactory\n\nYou can also use a `AsyncValidatorFactory`, it has the same behaviour as `ValidatorFactory`.\n\n```typescript\nclass Form {\n\n  @AsyncValidator([\n    AsyncValidatorFactory.of(\n      (service: MyService) =\u003e () =\u003e service.httpCall().pipe(map((result: string) =\u003e ({error: result}))),\n      [MyService]\n    )\n  ])\n  @FormControl()\n  public secondaryAddress: string;\n}\n```\n\n## FormChild\n\n```typescript\nimport { Component } from '@angular/core';\nimport { BuildForm, FormChild, NgxFormArray, NgxFormGroup } from '@paddls/ngx-form';\nimport { UserForm } from '../form/user.form';\nimport { CompanyForm } from '../form/company.form';\n\n@Component({\n  selector: 'app-root',\n  templateUrl: './app.component.html',\n  styleUrls: ['./app.component.scss']\n})\nexport class AppComponent {\n\n  @BuildForm(() =\u003e UserForm)\n  public userForm: NgxFormGroup\u003cUserForm\u003e;\n\n  @FormChild({attribute: 'userForm', path: 'skills'})\n  public skillForms: NgxFormArray\u003cstring\u003e;\n\n  @FormChild({attribute: 'userForm', path: 'companies'})\n  public companyForms: NgxFormArray\u003cCompanyForm\u003e;\n}\n```\n\n``@FormChild`` attribute can be used to access a subform of a built parent form directly. To do that, add an attribute\nwith the ``@FormChild`` decorator in the same class as your built parent form. Just specify the attribute name of the\nparent form with the ``attribute``\ndecorator parameter, the ``path`` of the form child you want to directly access, and you're done !\n\n## Form lifecycle\n\nA `NgxForm` object exposes the following methods :\n\n### getValue()\n\nReturns the current strongly typed value of the form.\n\n```typescript\nimport { Component } from '@angular/core';\nimport { BuildForm, NgxFormGroup } from '@paddls/ngx-form';\nimport { AddressForm } from './form/address.form';\n\n@Component({\n  selector: 'app-root',\n  templateUrl: './app.component.html'\n})\nexport class AppComponent {\n\n  @BuildForm(() =\u003e AddressForm)\n  public addressForm: NgxFormGroup\u003cAddressForm\u003e;\n\n  public onSubmit(): void {\n    console.log(this.addressForm.getValue());\n  }\n}\n```\n\n### setValue()\n\nSets a new value on the form. The behaviour of this method is similar to classic reactive forms.\n\n```typescript\nimport { Component } from '@angular/core';\nimport { BuildForm, NgxFormGroup } from '@paddls/ngx-form';\nimport { AddressForm } from './form/address.form';\n\n@Component({\n  selector: 'app-root',\n  templateUrl: './app.component.html'\n})\nexport class AppComponent {\n\n  @BuildForm(() =\u003e AddressForm)\n  public addressForm: NgxFormGroup\u003cAddressForm\u003e;\n\n  public constructor() {\n    this.addressForm.setValue(new AddressForm({city: 'New York City'}))\n  }\n}\n```\n\n### patchValue()\n\nPatches a new value on the form. The behaviour of this method is similar to classic reactive forms.\n\n```typescript\nimport { Component } from '@angular/core';\nimport { BuildForm, NgxFormGroup } from '@paddls/ngx-form';\nimport { AddressForm } from './form/address.form';\n\n@Component({\n  selector: 'app-root',\n  templateUrl: './app.component.html'\n})\nexport class AppComponent {\n\n  @BuildForm(() =\u003e AddressForm)\n  public addressForm: NgxFormGroup\u003cAddressForm\u003e;\n\n  public constructor() {\n    this.addressForm.patchValue(new AddressForm({city: 'New York City'}))\n  }\n}\n```\n\n### restore()\n\nRestores the form value to the initial value. Each control initial value is defined with the ``defaultValue`` attribute\navailable in the ``@FormControl()``, ``@FormGroup()`` and ``@FormArray()`` decorators.\n\n```typescript\nimport { Component } from '@angular/core';\nimport { BuildForm, NgxFormGroup } from '@paddls/ngx-form';\nimport { AddressForm } from './form/address.form';\n\n@Component({\n  selector: 'app-root',\n  templateUrl: './app.component.html'\n})\nexport class AppComponent {\n\n  @BuildForm(() =\u003e AddressForm)\n  public addressForm: NgxFormGroup\u003cAddressForm\u003e;\n\n  public onRestore(): void {\n    this.addressForm.restore();\n  }\n}\n```\n\n### empty()\n\nEmpties all values of the form. This calls ``reset()`` method on each form control and ``clear()`` method on all form\narrays.\n\n```typescript\nimport { Component } from '@angular/core';\nimport { BuildForm, NgxFormGroup } from '@paddls/ngx-form';\nimport { AddressForm } from './form/address.form';\n\n@Component({\n  selector: 'app-root',\n  templateUrl: './app.component.html'\n})\nexport class AppComponent {\n\n  @BuildForm(() =\u003e AddressForm)\n  public addressForm: NgxFormGroup\u003cAddressForm\u003e;\n\n  public onRestore(): void {\n    this.addressForm.empty();\n  }\n}\n```\n\n### cancel()\n\nCancels the last ``setValue()`` applied on the form.\n\n```typescript\nimport { Component } from '@angular/core';\nimport { BuildForm, NgxFormGroup } from '@paddls/ngx-form';\nimport { AddressForm } from './form/address.form';\n\n@Component({\n  selector: 'app-root',\n  templateUrl: './app.component.html'\n})\nexport class AppComponent {\n\n  @BuildForm(() =\u003e AddressForm)\n  public addressForm: NgxFormGroup\u003cAddressForm\u003e;\n\n  public onRestore(): void {\n    this.addressForm.cancel();\n  }\n}\n```\n\n### markAllAsDirty()\n\nSets all controls to ``DIRTY`` state.\n\n### markAllAsPending()\n\nSets all controls to ``PENDING`` state.\n\n### markAllAsPristine()\n\nSets all controls to ``PRISTINE`` state.\n\n### markAllAsUntouched()\n\nSets all controls to ``UNTOUCHED`` state.\n\n### add()\n\nAdds a new element to a form array. Prefer this method over ``push()`` method available on classic reactive forms as you\ndon't need to explicitly pass a new form control to add : by default, NgxForm will add a new instance of the type of the\nform array. You can set a default value to the new element and set a specific index.\n\n## DisableOn\n\nThe `@DisableOn()` decorator allows you to control when to enable/disable a control. The decorated control will be\ndisabled\nwhen the `Observable` passed as an argument is truthy, and enabled otherwise.\n\nIf you want to use a context from providers, you can use a `DisableOnFactory` and inject your providers.\n\nOptions can also be passed as a second argument of the decorator.\n\n```typescript\n\nclass UserForm {\n\n  @DisableOn(of(true))\n  @FormControl()\n  public surname: string;\n\n  @DisableOn(DisableOnFactory.of((provider: MyProvider) =\u003e provider.disableWithTimeout(), [MyProvider]))\n  @FormControl()\n  public disabledWithTimeoutAddress: string;\n\n  @DisableOn(DisableOnFactory.of((provider: MyProvider) =\u003e provider.disableWithTimeout(), [MyProvider]), {emitEvent: false})\n  @FormControl()\n  public disabledWithTimeoutAddressWithoutEvents: string;\n}\n```\n\n\u003e ⚠️ Any instance (built with `@BuildForm()`) of a form using this feature must\n\u003e have `unsubscribeOn` parameter provided in the config as follows :\n\n```typescript\nclass ConsumerComponent {\n\n  public readonly obs$: Observable\u003cany\u003e = EMPTY;\n\n  @BuildForm(() =\u003e UserForm, {unsubscribeOn: 'obs$'})\n  public form: NgxFormGroup\u003cUserForm\u003e;\n}\n```\n\n## OnValueChanges\n\nThe `@OnValueChanges()` decorator allows you to call a method when a form value is changed. Pass the\ncontrol name(s) for which you wish to listen to changes. If you wish to listen to changes on the whole\nform, you can apply the decorator without any parameters.\n\n```typescript\nclass SumForm {\n\n  @FormControl()\n  public a: number;\n\n  @FormControl()\n  public b: number;\n\n  @FormControl()\n  public sum: number;\n\n  @OnValueChanges(['a', 'b'])\n  public computeSum(instance: NgxFormGroup\u003cSumForm\u003e): void {\n    this.sum = this.a + this.b;\n    instance.setValue(this, {emitEvent: false});\n  }\n}\n```\n\n\u003e ⚠️ Any instance (built with `@BuildForm()`) of a form using this feature must\n\u003e have `unsubscribeOn` parameter provided in the config as follows :\n\n```typescript\nclass ConsumerComponent {\n\n  public readonly obs$: Observable\u003cany\u003e = EMPTY;\n\n  @BuildForm(() =\u003e UserForm, {unsubscribeOn: 'obs$'})\n  public form: NgxFormGroup\u003cUserForm\u003e;\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaddls%2Fngx-form","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpaddls%2Fngx-form","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaddls%2Fngx-form/lists"}