{"id":15694369,"url":"https://github.com/gaplo917/angular-typed-forms","last_synced_at":"2025-05-07T06:41:24.480Z","repository":{"id":38563662,"uuid":"258104036","full_name":"gaplo917/angular-typed-forms","owner":"gaplo917","description":"Typed Reactive Form. The missing piece of Angular.","archived":false,"fork":false,"pushed_at":"2023-01-26T19:30:10.000Z","size":874,"stargazers_count":7,"open_issues_count":23,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-31T07:22:02.999Z","etag":null,"topics":["angular","reactive-forms","typescript"],"latest_commit_sha":null,"homepage":"https://codesandbox.io/s/github/gaplo917/angular-typed-form-codesandbox/tree/master/","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/gaplo917.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}},"created_at":"2020-04-23T05:32:54.000Z","updated_at":"2021-09-22T13:48:16.000Z","dependencies_parsed_at":"2023-02-14T22:10:53.530Z","dependency_job_id":null,"html_url":"https://github.com/gaplo917/angular-typed-forms","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaplo917%2Fangular-typed-forms","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaplo917%2Fangular-typed-forms/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaplo917%2Fangular-typed-forms/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaplo917%2Fangular-typed-forms/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gaplo917","download_url":"https://codeload.github.com/gaplo917/angular-typed-forms/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252830577,"owners_count":21810771,"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","reactive-forms","typescript"],"created_at":"2024-10-03T18:57:26.047Z","updated_at":"2025-05-07T06:41:24.461Z","avatar_url":"https://github.com/gaplo917.png","language":"TypeScript","readme":"# Angular Typed Form\n\nThe missing piece of Angular.\n\n```bash\nnpm install @gaplo917/angular-typed-forms\n\n# OR\n\nyarn add @gaplo917/angular-typed-forms\n```\n\n## Features\n\nThis library uses a `ControlType` instead of `ValueType` as the `FormGroup` and `FormArray` type constraints and\nuses `infer` ([relatively new Typescript 2.8 feature](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html)) to extract the `ValueType` from the `ControlType`.\n\nThus, this implementation cannot be created earlier than Angular v6.1.\n\n| Features                                                                                                                                                                                                                                                                                                                                                                                                                        | Status |\n| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----: |\n| Strict Type Check                                                                                                                                                                                                                                                                                                                                                                                                               |   ✅   |\n| No Performance Degrade                                                                                                                                                                                                                                                                                                                                                                                                          |   ✅   |\n| **Zero** Force Type Cast Guarantee on .ts/.html                                                                                                                                                                                                                                                                                                                                                                                 |   ✅   |\n| Advance implementation to handle Complex Form Architecture([fullSync](https://github.com/gaplo917/angular-typed-forms/blob/6f80a5527cf75d1b40692f4e3359accc91568566/projects/angular-typed-forms/src/lib/forms/typed-form-group.ts#L85) \u0026 [partialSync](https://github.com/gaplo917/angular-typed-forms/blob/6f80a5527cf75d1b40692f4e3359accc91568566/projects/angular-typed-forms/src/lib/forms/typed-form-group.ts#L118) API) |   ✅   |\n| 100% Compatible to [Reactive Forms](https://angular.io/guide/reactive-forms)                                                                                                                                                                                                                                                                                                                                                    |   ✅   |\n| Enjoy slowly/partial migrate without learning a new library                                                                                                                                                                                                                                                                                                                                                                     |   ✅   |\n\n## Live Demo\n\n[![Edit gaplo917/angular-typed-form-codesandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/gaplo917/angular-typed-form-codesandbox/tree/master/?fontsize=14\u0026hidenavigation=1\u0026theme=dark)\n\n## Basic Usage (Standard ReactiveForm API)\n\n### Inject TypedFormBuilder / SimpleFormBuilder\n\n- **TypedFormBuilder** - Added Strong Typing on original Angular Reactive Form Modules, no breaking changes)\n- **SimpleFormBuilder** - Added advance implementation based on TypedForm to handle daily operation with minimal codes)\n\n```ts\nimport { TypedFormGroup, TypedFormControl, TypedFormBuilder, SimpleFormBuilder } from '@gaplo917/angular-typed-forms'\n\ninterface Foo {\n  first: TypedFormControl\u003cstring | null\u003e\n  last: TypedFormControl\u003cstring | null\u003e\n}\n\n// original Reactive Form modules\n@Component({\n  selector: 'app-demo',\n  templateUrl: './demo.component.html',\n  styleUrls: ['./demo.component.css'],\n})\nexport class DemoComponent implements OnInit {\n  form: TypedFormGroup\u003cFoo\u003e\n\n  constructor(private fb: TypedFormBuilder) {\n    this.form = fb.group({\n      first: fb.control(null),\n      last: fb.control(null),\n    })\n  }\n}\n\n// Simple Reactive Form\n@Component({\n  selector: 'app-simple',\n  templateUrl: './simple.component.html',\n  styleUrls: ['./simple.component.css'],\n})\nexport class SimpleComponent implements OnInit {\n  form: SimpleForm\u003cFoo\u003e\n\n  constructor(private fb: SimpleFormBuilder) {\n    this.form = fb.form({\n      first: fb.control(null),\n      last: fb.control(null),\n    })\n  }\n}\n```\n\n### TypedFormControl\n\n```ts\nnew TypedFormControl\u003cstring | null\u003e(null)\n// or using from TypedFormBuilder\nfb.control\u003cstring | null\u003e(null)\n```\n\n### TypedNumberFormControl\n\nThis will convert the string input to number before calling `setValue`. Any non-number return will return `null`.\nEnjoy getting a number type from the UI input.\n\n```ts\nnew TypedNumberFormControl\u003cnumber | null\u003e(null)\n// or using from  TypedFormBuilder\nfb.number\u003cnumber | null\u003e(null)\n```\n\n### TypedFormGroup\n\n```ts\nimport { TypedFormGroup, TypedFormControl, TypedFormBuilder } from '@gaplo917/angular-typed-forms'\n\ninterface Foo {\n  first: TypedFormControl\u003cstring | null\u003e\n  last: TypedFormControl\u003cstring | null\u003e\n}\nconst fb = new TypedFormBuilder()\nconst form: TypedFormGroup\u003cFoo\u003e = fb.group({\n  first: fb.control(null),\n  last: fb.control(null),\n})\n\nconsole.log(form.value) // {first: null, last: null}\n\nform.setValue({ first: 'Nancy', last: 'Drew' })\nconsole.log(form.value) // {first: 'Nancy', last: 'Drew'}\n```\n\n### TypedFormArray\n\n```ts\nimport { TypedFormArray, TypedFormControl, TypedFormBuilder } from '@gaplo917/angular-typed-forms'\n\nconst fb = new TypedFormBuilder()\n\nconst arr: TypedFormArray\u003cTypedFormControl\u003cstring | null\u003e\u003e = fb.array({\n  constructArrayItem: () =\u003e fb.control\u003cstring | null\u003e(null),\n  size: 2,\n})\nconsole.log(arr.value) // [null, null]\n\narr.setValue(['Nancy', 'Drew'])\nconsole.log(arr.value) // ['Nancy', 'Drew']\n```\n\n### TypedFormArray\u003cTypedFormGroup\u003cT\u003e\u003e (Table)\n\n```ts\nimport { TypedFormArray, TypedFormGroup, TypedFormControl, TypedFormBuilder } from '@gaplo917/angular-typed-forms'\n\ninterface Foo {\n  first: TypedFormControl\u003cstring | null\u003e\n  last: TypedFormControl\u003cstring | null\u003e\n}\n\nconst fb = new TypedFormBuilder()\n\nconst arr: TypedFormArray\u003cTypedFormGroup\u003cFoo\u003e\u003e = fb.array([{\n    fb.group\u003cFoo\u003e({\n      first: fb.control(null),\n      last: fb.control(null),\n    }),\n    fb.group\u003cFoo\u003e({\n      first: fb.control(null),\n      last: fb.control(null),\n    })\n}])\nconsole.log(arr.value) // [{ first: null, last: null }, { first: null, last: null }]\n\narr.setValue([\n  { first: 'Nancy', last: 'A' },\n  { first: 'Drew', last: 'B' },\n])\nconsole.log(arr.value) // [{ first: 'Nancy', last: 'A' }, { first: 'Drew, last: 'B' }]\n```\n\n## Advance Usage (Simple Module)\n\nThis is an **EXTRA** implementation based on Reactive Form Modules for a common scenario.\n\n- Added `fullSunc` \u0026 `partialSync` API that helps to sync remote API data to the form\n- Added Table / List Handy abstraction and handy api for your daily operations.\n\n### Added Types\n\n- SimpleForm\u003cT\u003e (TypedFormGroup)\n- SimpleFormArray\u003cT\u003e (TypedFormArray)\n- SimpleList\u003cT\u003e (SimpleFormArray\u003cTypedFormControl\u003cT\u003e\u003e)\n- SimpleTable\u003cT\u003e (SimpleFormArray\u003cFormGroup\u003cT\u003e\u003e)\n- SimpleFormBuilder\n\nHighly recommend creating a dedicated `class` to represent a complex form.\n\n```ts\nimport { SimpleTable, SimpleFormBuilder, TypedFormControl, TypedNumberFormControl } from '@gaplo917/angular-typed-forms'\n\ninterface UserTableType {\n  id: TypedFormControl\u003cstring | null\u003e\n  username: TypedFormControl\u003cstring | null\u003e\n  birth: TypedFormControl\u003cDate | null\u003e\n  isStudent: TypedFormControl\u003cboolean\u003e\n  age?: TypedNumberFormControl\u003cnumber | null\u003e\n  // nested form\n  addresses: SimpleTable\u003c{\n    address1: TypedFormControl\u003cstring | null\u003e\n    address2: TypedFormControl\u003cstring | null\u003e\n    address3: TypedFormControl\u003cstring | null\u003e\n  }\u003e\n}\n\n/**\n * SimpleTable is equivalent to TypedFormArray\u003cTypedFormGroup\u003cUserTableType\u003e\u003e but with more pre-defined API\n */\nexport class UserTable extends SimpleTable\u003cUserTableType\u003e {\n  constructor(private fb: SimpleFormBuilder) {\n    super({\n      constructRow: (index: number) =\u003e\n        fb.form({\n          id: fb.control(String('ID-' + index)),\n          username: fb.control(null),\n          birth: fb.control(null),\n          isStudent: fb.control\u003cboolean\u003e(false),\n          addresses: fb.table({\n            constructRow: () =\u003e\n              fb.form({\n                address1: fb.control(null),\n                address2: fb.control(null),\n                address3: fb.control(null),\n              }),\n            size: 1,\n          }),\n        }),\n      size: 2,\n    })\n  }\n}\n```\n\n### Extra `fullSunc` \u0026 `partialSync` API\n\nFully-typed and synchronize the children form control with the value recursively.\nBefore setting the value of the `FormArray`, it tries to add/remove necessary `Control`\naccording to the value.\n\n`fullSync` use `setValue` internally\n`partialSync` use `patchValue` internally\n\n```ts\nimport { TypedFormGroup, TypedFormControl, SimpleFormBuilder } from '@gaplo917/angular-typed-forms'\n\ninterface Bar {\n  something: TypedFormControl\u003cstring | null\u003e\n}\n\ninterface Foo {\n  first: TypedFormControl\u003cstring | null\u003e\n  last: TypedFormControl\u003cstring | null\u003e\n  bar: TypedFormGroup\u003cBar\u003e\n}\n\nconst fb = new SimpleFormBuilder()\n\nconst form = fb.formArray\u003cFoo\u003e([])\n\nconsole.log(form.value) // []\n\n// full strict type check\nform.fullSync([{ first: 'Nancy', last: 'Drew', bar: { something: 'happen' } }]) // OK\nform.fullSync([{ first: 'Nancy', last: 'Drew', bar: {} }]) // Not compile, missing `something`\nform.fullSync([{ first: 'Nancy', last: 'Drew', bar: { something: 'happen' }, unknownKey: 'not suppose here' }]) // Not compile, redundant `unknownKey`\n\nconsole.log(form.value) // {first: 'Nancy', last: 'Drew', bar: { something: 'happen' }}\n\n// partial type check\nform.partialSync([{ first: 'Nancy2', last: 'Drew2' }]) // OK\nform.partialSync([{ first: 'Nancy', last: 'Drew', unknownKey: 'not suppose here' }]) // Not compile, redundant `unknownKey`\n\nconsole.log(form.value) // {first: 'Nancy2', last: 'Drew2', bar: { something: 'happen' }}, `bar` remain unchanged\n```\n\n### Full Example\n\n```ts\nimport { SimpleTable, SimpleFormBuilder, TypedFormControl, TypedNumberFormControl } from '@gaplo917/angular-typed-forms'\n\ninterface UserTableType {\n  id: TypedFormControl\u003cstring | null\u003e\n  username: TypedFormControl\u003cstring | null\u003e\n  birth: TypedFormControl\u003cDate | null\u003e\n  isStudent: TypedFormControl\u003cboolean\u003e\n  age?: TypedNumberFormControl\u003cnumber | null\u003e\n  // nested form\n  addresses: SimpleTable\u003c{\n    address1: TypedFormControl\u003cstring | null\u003e\n    address2: TypedFormControl\u003cstring | null\u003e\n    address3: TypedFormControl\u003cstring | null\u003e\n  }\u003e\n}\n\n/**\n * SimpleTable is equivalent to TypedFormArray\u003cTypedFormGroup\u003cUserTableType\u003e\u003e but with more pre-defined API\n */\nexport class UserTable extends SimpleTable\u003cUserTableType\u003e {\n  constructor(private fb: SimpleFormBuilder) {\n    super({\n      constructRow: (index: number) =\u003e\n        fb.form({\n          id: fb.control(String('ID-' + index)),\n          username: fb.control(null),\n          birth: fb.control(null),\n          isStudent: fb.control\u003cboolean\u003e(false),\n          addresses: fb.table({\n            constructRow: () =\u003e\n              fb.form({\n                address1: fb.control(null),\n                address2: fb.control(null),\n                address3: fb.control(null),\n              }),\n            size: 1,\n          }),\n        }),\n      size: 2,\n    })\n  }\n}\n\n@Component({\n  selector: 'app-demo',\n  templateUrl: './demo.component.html',\n  styleUrls: ['./demo.component.css'],\n})\nexport class DemoComponent {\n  userTable: UserTable\n\n  constructor(private fb: SimpleFormBuilder) {\n    this.userTable = new UserTable(fb)\n  }\n\n  fullSync() {\n    // fullSync requires strict type match of the value you input\n    // this method will use the values to sync the controls\n    // internally match the numbers of control before use `FormArray.setValue`\n    this.userTable.fullSync([\n      {\n        id: '42e6f1fe-93bd-4993-ab1b-ebaec9ee8d10',\n        username: 'Gary',\n        birth: new Date('2000-01-01'),\n        isStudent: false,\n        // because `age` is optional in the definition\n        // age: 1,\n        addresses: [\n          {\n            address1: 'HK',\n            address2: 'Earth',\n            address3: '',\n          },\n        ],\n      },\n    ])\n  }\n\n  partialSync() {\n    // partialSync only require Partial\u003cT\u003e, but it will also sync them number of controls\n    // this method will use the values to sync the controls\n    // internally match the numbers of control before use `FormArray.patchValue`\n    this.userTable.partialSync([\n      {\n        id: '42e6f1fe-93bd-4993-ab1b-ebaec9ee8d10',\n        username: 'Gary',\n      },\n    ])\n  }\n\n  reset() {\n    this.userTable.reset()\n  }\n}\n```\n\n## Local Development\n\nYou can use [`npm link`](https://docs.npmjs.com/cli/link.html) to develop this library locally without pushing every change npm registry.\n\n1. Build this library first.\n2. Go into the `dist/angular-typed-forms` and run `npm link`.\n3. Go into your project which depend on this library and run `npm link @gaplo917/angular-typed-forms`\n4. Run `ng build --watch` in the root of this library (optional)\n5. Done\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgaplo917%2Fangular-typed-forms","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgaplo917%2Fangular-typed-forms","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgaplo917%2Fangular-typed-forms/lists"}