{"id":26358413,"url":"https://github.com/lazarljubenovic/ngx-common-forms","last_synced_at":"2025-03-16T14:27:52.645Z","repository":{"id":25702105,"uuid":"105441832","full_name":"lazarljubenovic/ngx-common-forms","owner":"lazarljubenovic","description":"A util Angular module for handling most common use-cases for forms.","archived":false,"fork":false,"pushed_at":"2023-01-07T03:13:08.000Z","size":2277,"stargazers_count":6,"open_issues_count":29,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2023-02-27T17:23:40.058Z","etag":null,"topics":["angular","forms"],"latest_commit_sha":null,"homepage":"https://stackblitz.com/edit/ngx-common-forms","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/lazarljubenovic.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}},"created_at":"2017-10-01T12:50:24.000Z","updated_at":"2020-02-26T02:45:38.000Z","dependencies_parsed_at":"2022-08-29T00:03:32.711Z","dependency_job_id":null,"html_url":"https://github.com/lazarljubenovic/ngx-common-forms","commit_stats":null,"previous_names":[],"tags_count":null,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lazarljubenovic%2Fngx-common-forms","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lazarljubenovic%2Fngx-common-forms/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lazarljubenovic%2Fngx-common-forms/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lazarljubenovic%2Fngx-common-forms/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lazarljubenovic","download_url":"https://codeload.github.com/lazarljubenovic/ngx-common-forms/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243881202,"owners_count":20362919,"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"],"created_at":"2025-03-16T14:27:52.044Z","updated_at":"2025-03-16T14:27:52.638Z","avatar_url":"https://github.com/lazarljubenovic.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ngx-common-forms\n\nForms are straight-forward... if user doesn't make any mistakes. Throw in all (client-side and server-side) the validation messages, the loading spinner, disable submission while the form is already submitting, and you end up with a huge pile of mess... in every. single. form. again. and. again.\n\nIf you have a fairy common form, without some extremely crazy stuff, this package will handle all this for you, while still leaving enough room for customization. All while following regular Angular form-related principles.\n\n# [Stack Blitz Demo](https://stackblitz.com/edit/ngx-common-forms)\n\n# Installation\n\n```\n# with yarn\nyarn add ngx-common-forms\n\n# with npm\nnpm i ngx-common-forms\n```\n\n# Feature highlights\n\n- Submitting an invalid form immediately triggers displaying **declarative client-side validation**.\n- If a **server-side error** occurs, the errors are automatically inserted to the form.\n- The first field marked as invalid will immediately **receive focus** to aid your users. (Also great for long forms as browsers will automatically scroll to the field.)\n- Leverage the \"loading\" observable to **show a spinner** in the submit button (or some other indicator).\n- While in loading state, form **cannot be re-submitted**.\n- Your **API call is a simple function** which takes the forum value and returns an observable. You can pass it in directly from your services.\n\n# Basic usage\n\n## Import the module\n\n```typescript\n@NgModule({\n  // ...\n  imports: [\n    // ...\n    CommonFormModule.forRoot(),\n    // ...\n  ],\n  // ...\n})\n```\n\n## Bind the API call to `commonForm`\n\n```typescript\n// The good old service for trigger an endpoint\n\n@Injectable()\nexport class ApiService {\n  public logIn({ username, passowrd }): Observable\u003cany\u003e {\n    return this.http.post(\n      `https://api.example/log-in`,\n      { username, password },\n    );\n  }\n}\n```\n\n```typescript\n@Component({ /* ... */ })\nexport class MyComponent {\n  constructor(private fb: FormBuilder,\n              public api: ApiService) { // inject the service\n  }\n  \n  // A regular reactive form, with regular validation messages.\n  public form = this.fb.group({\n    username: ['', [Validators.required]],\n    password: ['', [Validators.required, Validators.minLength(6)]],\n  });\n\n  // Instead of `(ngSubmit)`, you listen to `(commonFormSubmit)` and react to form submission.\n  // For example, you can redirect here or display a snackbar indicating success,\n  // or further react to errors in submission.\n  public onSubmit(response$: Observable\u003cany\u003e) {\n    response$.subscribe(response =\u003e {\n      console.log(response);\n    });\n  }\n}\n```\n\n```html\n\u003cform [formGroup]=\"form\"\n      [commonForm]=\"api.logIn.bind(api)\"\n      (commonFormSubmit)=\"onSubmit($event)\"\n\u003e\n\n  \u003clabel\u003e\n    \u003cspan\u003eUsername\u003c/span\u003e\n    \u003cinput type=\"text\" formControlName=\"username\"\u003e\n  \u003c/label\u003e\n  \u003cdiv sins=\"username\"\u003e\n    \u003c!-- The library handles displaying these at the right time. --\u003e\n    \u003c!-- All you have to do is type them out (and give them styles, animations, etc). --\u003e\n    \u003cspan *sin=\"'required'\"\u003eUsername is required.\u003c/span\u003e\n    \u003cspan *sin=\"'serverError' let msg\"\u003e{{ msg }}\u003c/span\u003e\n  \u003c/div\u003e\n  \n  \u003clabel\u003e\n    \u003cspan\u003ePassword\u003c/span\u003e\n    \u003cinput type=\"password\" formControlName=\"password\"\u003e\n  \u003c/label\u003e\n  \u003cdiv sins=\"password\"\u003e\n    \u003cspan *sin=\"'required'\"\u003ePassword is required.\u003c/span\u003e\n    \u003cspan *sin=\"'minlength'\"\u003ePassword must have at least 6 characters.\u003c/span\u003e\n    \u003cspan *sin=\"'serverError' let msg\"\u003e{{ msg }}\u003c/span\u003e\n    \u003c!-- If the server responds with an error, it will be added to form's errors under key \"serverError\". --\u003e\n    \u003c!-- The exact message is also available via a simple binding, as above. --\u003e  \n  \u003c/div\u003e\n\n  \u003cbutton type=\"submit\"\u003eLog in\u003c/button\u003e\n\n\u003c/form\u003e\n```\n\n## Button spinner\n\nSee `src/app/button.component.ts`.\n\n# Finer configuration\n\nDifferent apps will have different kinds of forms, but even more different kinds of APIs. Each team has established habits which they follow across every project. Hence, `ngx-common-forms` offers configuration settings to match your API.\n\nThe default configuration is supplied as an object to the `forRoot` method call when importing the module. Here's the typing information for this object.\n\n```typescript\nexport interface CommonFormConfigObject {\n  propagateErrors?: boolean;\n  transform?: CommonFormTransform;\n  isValidationError?: CommonFormIsValidationError;\n  transformError?: CommonFormTransformError;\n  request?: CommonFormRequest;\n}\n\nexport type CommonFormTransform = (formValue: any) =\u003e any;\nexport type CommonFormIsValidationError = (response: HttpErrorResponse) =\u003e boolean\nexport type CommonFormTransformError = (formValue: any) =\u003e FlatServerErrors;\nexport type CommonFormRequest\u003cT = any\u003e = (formValue: T) =\u003e Observable\u003cT\u003e;\n\nexport interface FlatServerErrors {\n  [path: string]: string;\n}\n```\n\nEach of these keys can also be configured individually on per-form basis, which allows you to overwrite these global settings in case some of the forms is one-of-a-kind.\n\n## Request\n\nThe request option doesn't make much sense to be configured globally, since it's the function which takes form value and returns an observable. Typically this is what you already have in your service methods. \n \nFor providing it as an input, you can use the `[request]` input. However, since this is the main which you always use, there's an alias `[commonForm]`. Since `commonForm` is also the name of the directive, you write it only once this way, saving keystrokes.\n\n## Transform\n\nBefore you send the form value to the server, you might want to transform it a bit. This can be done in the request function itself, but `ngx-common-forms` also allows you to provide a separate transforming function. Basically, this means that instead of `request(formValue)`, the library does `request(transform(formValue))`.\n\n## Is validation error\n\nUse `isValidationError` to pass in a predicate\u003csup\u003e†\u003c/sup\u003e used to determine if the error which occurred during the request is supposed to be queried for validation errors inside. By default this checks for the 422 status in HTTP response.\n\nThis is how you tell the lib which type of error responses you consider \"validation errors\". For example, a 5xx and 4xx error should probably be handled differently.\n\n\u003csup\u003e†\u003c/sup\u003e A predicate is a function which returns boolean value.\n\n## Transform error\n\nWhen the lib understands which types of errors should be treated as validation errors, it must also know how to interpret the payload from the response and apply it to the form (in order to show validation messages). `transformError` is a function which describes how the server response should be mapped to `FlatServerErrors`, which has a simple signature `{ [path: string]: string }`.\n\nHere's an example of a flat error.\n\n```javascript\n{\n  'username': 'Username must not contain spaces',\n  'location': 'Invalid location, see details below',\n  'location.zip': 'This field must contain a valid zip code',\n}\n```\n\n## Propagate errors\n\nThe `propagateErrors` is a boolean flag determining whether received error will be propagated, i.e. if the observable you receive from `(commonFormSubmit)` will throw with your server error. In most cases you do not need this as errors caught with `isValidationError` will be handled by the lib, but sometimes you might want to override it with custom logic tied too closely to the component.\n\n---\n\n\u003e Built with `@angular/cli@7.3.1`.\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flazarljubenovic%2Fngx-common-forms","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flazarljubenovic%2Fngx-common-forms","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flazarljubenovic%2Fngx-common-forms/lists"}