{"id":16244597,"url":"https://github.com/endykaufman/ngx-dynamic-form-builder","last_synced_at":"2025-04-09T16:06:22.361Z","repository":{"id":29258072,"uuid":"120668013","full_name":"EndyKaufman/ngx-dynamic-form-builder","owner":"EndyKaufman","description":"FormBuilder + class-transformer + class-validator = dynamic form group builder for Angular","archived":false,"fork":false,"pushed_at":"2025-03-15T20:23:54.000Z","size":33334,"stargazers_count":118,"open_issues_count":25,"forks_count":29,"subscribers_count":3,"default_branch":"develop","last_synced_at":"2025-04-09T16:06:10.643Z","etag":null,"topics":["angular","angular10","auto","builder","class","class-transformer","class-validator","demo","dynamic","form","form-builder","formarray","formgroup","object","transform","transformer","validation","validator"],"latest_commit_sha":null,"homepage":"https://endykaufman.github.io/ngx-dynamic-form-builder","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/EndyKaufman.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2018-02-07T20:31:32.000Z","updated_at":"2025-02-03T18:48:01.000Z","dependencies_parsed_at":"2024-04-30T09:42:47.765Z","dependency_job_id":"8f592bdb-604f-4de9-bc25-803772b01244","html_url":"https://github.com/EndyKaufman/ngx-dynamic-form-builder","commit_stats":{"total_commits":240,"total_committers":6,"mean_commits":40.0,"dds":"0.11250000000000004","last_synced_commit":"1c7b8ddfc35519b2c0c8b68ce09e2f6e3b456554"},"previous_names":[],"tags_count":54,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EndyKaufman%2Fngx-dynamic-form-builder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EndyKaufman%2Fngx-dynamic-form-builder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EndyKaufman%2Fngx-dynamic-form-builder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EndyKaufman%2Fngx-dynamic-form-builder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/EndyKaufman","download_url":"https://codeload.github.com/EndyKaufman/ngx-dynamic-form-builder/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248065288,"owners_count":21041871,"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","angular10","auto","builder","class","class-transformer","class-validator","demo","dynamic","form","form-builder","formarray","formgroup","object","transform","transformer","validation","validator"],"created_at":"2024-10-10T14:19:51.231Z","updated_at":"2025-04-09T16:06:22.332Z","avatar_url":"https://github.com/EndyKaufman.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ngx-dynamic-form-builder\n\n[![Build Status](https://travis-ci.org/EndyKaufman/ngx-dynamic-form-builder.svg?branch=master)](https://travis-ci.org/EndyKaufman/ngx-dynamic-form-builder)\n[![npm version](https://badge.fury.io/js/ngx-dynamic-form-builder.svg)](https://badge.fury.io/js/ngx-dynamic-form-builder)\n[![monthly downloads](https://badgen.net/npm/dm/ngx-dynamic-form-builder)](https://www.npmjs.com/package/ngx-dynamic-form-builder)\n\n[FormBuilder](https://angular.io/api/forms/FormBuilder) + [class-transformer-global-storage](https://github.com/petrzjunior/class-transformer) + [class-validator-multi-lang](https://github.com/endykaufman/class-validator-multi-lang) = dynamic form group builder for [Angular](https://angular.io)\n\n## Installation\n\n```bash\nnpm i --save class-transformer-global-storage class-validator-multi-lang ngx-dynamic-form-builder\n```\n\n## BREAKING CHANGE !!!\n\nVersion above 2 has a completely rewritten code, partially backwards compatible\n\nNow `@Expose` and `@Exclude` decorators are used to define model fields, the new version is rigidly dependent on class-transform\n\nDependencies are not used original, but forks with additional necessary properties, when using this library, you need to replace all original imports with forks with modifications\n\nFork [class-validator-multi-lang](https://github.com/EndyKaufman/class-validator-multi-lang) - adds translation capability for errors (PR:[https://github.com/typestack/class-validator/pull/743](https://github.com/typestack/class-validator/pull/743))\n\nFork [class-transformer-global-storage](https://github.com/petrzjunior/class-transformer) - adds the ability to get meta information about all used classes (PR:[https://github.com/typestack/class-transformer/pull/929](https://github.com/typestack/class-transformer/pull/929))\n\nFor correct parse metadata, need remove `compilerOptions.downlevelIteration` and append `compilerOptions.emitDecoratorMetadata: true` in `tsconfig.json`\n\n## Links\n\n[Demo](https://endykaufman.github.io/ngx-dynamic-form-builder) - Demo application with ngx-dynamic-form-builder.\n\n[Stackblitz](https://stackblitz.com/edit/ngx-dynamic-form-builder) - Simply sample of usage on https://stackblitz.com\n\n## Usage\n\ncompany.ts\n\n```typescript\nimport { Validate, IsNotEmptym } from 'class-validator-multi-lang';\nimport { TextLengthMore15 } from '../utils/custom-validators';\nimport { marker } from '@ngneat/transloco-keys-manager/marker';\nimport { Expose, Type } from 'class-transformer-global-storage';\n\nexport class Company {\n  @Expose()\n  id: number;\n\n  @Validate(TextLengthMore15, {\n    message: marker('The company name must be longer than 15'),\n  })\n  @IsNotEmpty()\n  @Expose()\n  name: string;\n\n  constructor(data?: any) {\n    if (data === undefined) {\n      data = {};\n    }\n    this.id = data.id;\n    this.name = data.name;\n  }\n}\n```\n\napp.module.ts\n\n```typescript\nimport { FormsModule, ReactiveFormsModule } from '@angular/forms';\nimport { CompanyPanelComponent } from './company-panel.component';\n\n@NgModule({\n  imports: [\n    ...\n    FormsModule,\n    ReactiveFormsModule,\n    ...\n  ],\n  declarations: [\n    ...\n    CompanyPanelComponent,\n    ...\n  ],\n  ...\n})\nexport class AppModule {}\n```\n\ncompany-panel.component.html\n\n```html\n\u003cform [formGroup]=\"form\" *ngIf=\"form?.customValidateErrors | async as errors\" novalidate\u003e\n  \u003cinput formControlName=\"name\" placeholder=\"Name\" /\u003e\n  \u003cp *ngIf=\"errors.name?.length\"\u003eError: {{errors.name[0]}}\u003c/p\u003e\n  \u003cp\u003eForm status: {{ form.status | json }}\u003c/p\u003e\n  \u003cp\u003eForm class-validator-multi-lang errors: {{errors|json}}\u003c/p\u003e\n  \u003cp *ngIf=\"savedItem\"\u003eSaved item: {{savedItem|json}}\u003c/p\u003e\n  \u003cbutton (click)=\"onLoadClick()\"\u003eLoad\u003c/button\u003e\n  \u003cbutton (click)=\"onClearClick()\"\u003eClear\u003c/button\u003e\n  \u003cbutton (click)=\"onSaveClick()\" [disabled]=\"!form.valid\"\u003eSave\u003c/button\u003e\n\u003c/form\u003e\n```\n\ncompany-panel.component.ts\n\n```typescript\nimport { DynamicFormGroup, DynamicFormBuilder } from 'ngx-dynamic-form-builder';\nimport { Company } from './../../shared/models/company';\nimport { Input, Component } from '@angular/core';\nimport { Validators } from '@angular/forms';\n\n@Component({\n  selector: 'company-panel',\n  templateUrl: './company-panel.component.html',\n})\nexport class CompanyPanelComponent {\n  form: DynamicFormGroup\u003cCompany\u003e;\n\n  @Input()\n  item = new Company({\n    id: 11,\n    name: '123456789012345',\n  });\n\n  fb = new DynamicFormBuilder();\n\n  savedItem?: Company;\n\n  constructor() {\n    this.form = this.fb.rootFormGroup(Company, {\n      name: '',\n    });\n  }\n  onLoadClick(): void {\n    this.savedItem = undefined;\n    this.form.object = this.item;\n  }\n  onClearClick(): void {\n    this.savedItem = undefined;\n    this.form.object = new Company();\n  }\n  onSaveClick(): void {\n    if (this.form.valid) {\n      this.savedItem = this.form.object;\n    } else {\n      this.savedItem = undefined;\n    }\n  }\n}\n```\n\ncustom-validators.ts\n\n```typescript\nimport { ValidatorConstraintInterface, ValidatorConstraint } from 'class-validator-multi-lang';\n\n@ValidatorConstraint()\nexport class TextLengthMore15 implements ValidatorConstraintInterface {\n  validate(text: string) {\n    return text ? text.length \u003e 15 : false;\n  }\n}\n```\n\n## Support multi-language translate validation errors (I18n)\n\nBecause multi-language supported in class-validator-multi-lang, now ngx-dynamic-form-builder also support this feature\n\nset validation messages as settings when create form group\n\n```typescript\nthis.form = this.fb.rootFormGroup(\n  Company,\n  {\n    name: '',\n  },\n  {\n    classValidatorOptions: {\n      messages: {\n        'The company name must be longer than 15': 'company name must be longer than 15 (translate on other language)',\n      },\n    },\n  }\n);\n```\n\nset validation messages on runtime after for exists form group\n\n```typescript\nthis.form.patchDynamicFormBuilderOptions({\n  classValidatorOptions: {\n    messages: {\n      'The company name must be longer than 15': 'company name must be longer than 15 (translate on other language)',\n    },\n  },\n});\n```\n\nset translate property name in error\n\n```typescript\nthis.form.patchDynamicFormBuilderOptions({\n  classValidatorOptions: {\n    titles: {\n      regionNum: 'number of region (translate property name in error on other language)',\n    },\n  },\n});\n```\n\nset validation messages and properties name global for all instance of form group in project\n\n```typescript\nsetGlobalDynamicFormBuilderOptions({\n  classValidatorOptions: {\n    messages: {\n      'The company name must be longer than 15': 'company name must be longer than 15 (translate on other language)',\n    },\n    titles: {\n      regionNum: 'number of region (translate property name in error on other language)',\n    },\n  },\n});\n```\n\n## Observable Errors\n\nThe customValidateErrors property can be subscribed for cases in which your code should act on changes in errors\n\ncompany-panel.component.html\n\n```html\n\u003cform [formGroup]=\"form\" *ngIf=\"form?.customValidateErrors | async as errors\" novalidate\u003e\n  \u003cinput formControlName=\"name\" placeholder=\"Name\" /\u003e\n  \u003cp *ngIf=\"errors.name?.length\"\u003eError: {{errors.name[0]}}\u003c/p\u003e\n  \u003cp\u003eForm status: {{ form.status | json }}\u003c/p\u003e\n  \u003cp\u003eObservable validation errors: {{errors|json}}\u003c/p\u003e\n  \u003cp *ngIf=\"savedItem\"\u003eSaved item: {{savedItem|json}}\u003c/p\u003e\n  \u003cbutton (click)=\"onLoadClick()\"\u003eLoad\u003c/button\u003e\n  \u003cbutton (click)=\"onClearClick()\"\u003eClear\u003c/button\u003e\n  \u003cbutton (click)=\"onSaveClick()\" [disabled]=\"!form.valid\"\u003eSave\u003c/button\u003e\n\u003c/form\u003e\n```\n\ncompany-panel.component.ts\n\n```typescript\nimport { DynamicFormGroup, DynamicFormBuilder } from 'ngx-dynamic-form-builder';\nimport { Company } from './../../shared/models/company';\nimport { Input, Component } from '@angular/core';\nimport { Validators } from '@angular/forms';\nimport { Subscription } from 'rxjs';\n\n@Component({\n  selector: 'company-panel',\n  templateUrl: './company-panel.component.html',\n})\nexport class CompanyPanelComponent implements onDestroy {\n  form: DynamicFormGroup\u003cCompany\u003e;\n\n  @Input()\n  item = new Company({\n    id: 11,\n    name: '123456789012345',\n  });\n\n  @Input()\n  strings = Company.strings;\n\n  fb = new DynamicFormBuilder();\n\n  savedItem?: Company;\n\n  errorChangeSubscription: Subscription;\n\n  constructor() {\n    this.form = this.fb.rootFormGroup(Company, {\n      name: '',\n    });\n\n    this.errorChangeSubscription = this.form.customValidateErrors.subscribe((allErrors) =\u003e {\n      console.log(`Errors changed: ${allErrors}`);\n    });\n  }\n  ngOnDestroy() {\n    if (this.errorChangeSubscription != null \u0026\u0026 this.errorChangeSubscription.closed === false) {\n      this.errorChangeSubscription.unsubscribe();\n    }\n  }\n  onLoadClick(): void {\n    this.savedItem = undefined;\n    this.form.object = this.item;\n    this.form.validateAllFormFields();\n  }\n  onClearClick(): void {\n    this.savedItem = undefined;\n    this.form.object = new Company();\n    this.form.validateAllFormFields();\n  }\n  onSaveClick(): void {\n    this.form.validateAllFormFields();\n    if (this.form.valid) {\n      this.savedItem = this.form.object;\n    } else {\n      this.savedItem = undefined;\n    }\n  }\n}\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendykaufman%2Fngx-dynamic-form-builder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fendykaufman%2Fngx-dynamic-form-builder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendykaufman%2Fngx-dynamic-form-builder/lists"}