{"id":13580356,"url":"https://github.com/ngneat/error-tailor","last_synced_at":"2025-05-16T01:05:15.446Z","repository":{"id":36953713,"uuid":"268797519","full_name":"ngneat/error-tailor","owner":"ngneat","description":"🦄 Making sure your tailor-made error solution is seamless!","archived":false,"fork":false,"pushed_at":"2024-05-28T23:55:34.000Z","size":3446,"stargazers_count":487,"open_issues_count":34,"forks_count":30,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-05-07T18:15:34.830Z","etag":null,"topics":["angular","errors","form","form-errors"],"latest_commit_sha":null,"homepage":"https://netbasal.com/make-your-angular-forms-error-messages-magically-appear-1e32350b7fa5","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/ngneat.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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},"funding":{"github":"ngneat"}},"created_at":"2020-06-02T12:40:21.000Z","updated_at":"2025-04-29T06:29:12.000Z","dependencies_parsed_at":"2024-11-05T19:55:02.272Z","dependency_job_id":null,"html_url":"https://github.com/ngneat/error-tailor","commit_stats":{"total_commits":108,"total_committers":17,"mean_commits":6.352941176470588,"dds":0.6111111111111112,"last_synced_commit":"85d1a00a05ed7677a9032bbb04c8a02c57379e76"},"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngneat%2Ferror-tailor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngneat%2Ferror-tailor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngneat%2Ferror-tailor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ngneat%2Ferror-tailor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ngneat","download_url":"https://codeload.github.com/ngneat/error-tailor/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254448579,"owners_count":22072764,"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","errors","form","form-errors"],"created_at":"2024-08-01T15:01:50.174Z","updated_at":"2025-05-16T01:05:15.424Z","avatar_url":"https://github.com/ngneat.png","language":"TypeScript","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg width=\"20%\" height=\"20%\" src=\"./logo.svg\"\u003e\n\u003c/p\u003e\n\n\u003cbr /\u003e\n\n\u003e Making sure your tailor-made error solution is seamless!\n\n[![MIT](https://img.shields.io/packagist/l/doctrine/orm.svg?style=flat-square)]()\n[![commitizen](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg?style=flat-square)]()\n[![PRs](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)]()\n[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)\n[![All Contributors](https://img.shields.io/badge/all_contributors-0-orange.svg?style=flat-square)](#contributors-)\n[![ngneat](https://img.shields.io/badge/@-ngneat-383636?style=flat-square\u0026labelColor=8f68d4)](https://github.com/ngneat/)\n[![spectator](https://img.shields.io/badge/tested%20with-spectator-2196F3.svg?style=flat-square)]()\n\nThe Error Tailor offers seamless handling of form errors, saving you the trouble of repeating the error boilerplate.\nIt's fully customizable, so you can control when, where, and how each form field's errors are displayed.\nSit back, relax, and let the Error Tailor do all the work!\n\n\u003cimg src=\"./demo.gif\"\u003e\n\n\u003ca href=\"https://www.buymeacoffee.com/basalnetanel\" target=\"_blank\"\u003e\u003cimg src=\"https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png\" alt=\"Buy Me A Coffee\" style=\"height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;\" \u003e\u003c/a\u003e\n\n## Getting Started\n\nRun `npm install @ngneat/error-tailor` and add the imports to your application:\n\n\u003c!-- prettier-ignore-start --\u003e\n```ts\nimport { provideErrorTailorConfig } from '@ngneat/error-tailor';\n\nbootstrapApplication(AppComponent, {\n  providers: [\n    provideErrorTailorConfig({\n      errors: {\n        useValue: {\n          required: 'This field is required',\n          minlength: ({ requiredLength, actualLength }) =\u003e \n                      `Expect ${requiredLength} but got ${actualLength}`,\n          invalidAddress: error =\u003e `Address isn't valid`\n        }\n      }\n    })\n  ]\n})\n```\n\u003c!-- prettier-ignore-end --\u003e\n\nThe `errors` config property takes a partial `Provider`, that should provide a `HashMap\u003cstring | (err:any) =\u003e string\u003e` that is an object with keys corresponding to the errors name that you want to handle, and values that can be a simple string, or function that return a string used as error message to be shown.\nNow the only thing you need to add is the `errorTailor` directive to your form:\n\n```html\n\u003cform [formGroup]=\"form\" errorTailor\u003e\n  \u003cdiv class=\"form-group\"\u003e\n    \u003cinput class=\"form-control\" formControlName=\"name\" placeholder=\"Name\" /\u003e\n  \u003c/div\u003e\n\n  \u003csection formGroupName=\"address\"\u003e\n    \u003cdiv class=\"form-group\"\u003e\n      \u003cinput class=\"form-control\" formControlName=\"city\" placeholder=\"City\" /\u003e\n    \u003c/div\u003e\n\n    \u003cdiv class=\"form-group\"\u003e\n      \u003cinput class=\"form-control\" formControlName=\"country\" placeholder=\"Country\" /\u003e\n    \u003c/div\u003e\n  \u003c/section\u003e\n\n  \u003cdiv class=\"form-group\"\u003e\n    \u003cselect formControlName=\"animal\" class=\"form-control\"\u003e\n      \u003coption *ngFor=\"let option of options; index as index\" [ngValue]=\"option\"\u003e\n        {{ option.label }}\n      \u003c/option\u003e\n    \u003c/select\u003e\n  \u003c/div\u003e\n\n  \u003cbutton class=\"btn btn-success\"\u003eSubmit\u003c/button\u003e\n\u003c/form\u003e\n```\n\n```ts\nimport { errorTailorImports } from '@ngneat/error-tailor';\n\n@Component({\n  selector: 'app-root',\n  standalone: true,\n  imports: [errorTailorImports, ReactiveFormsModule]\n})\nexport class AppComponent {\n  private builder = inject(FormBuilder);\n  form: FormGroup;\n\n  ngOnInit() {\n    this.form = this.builder.group({\n      name: ['', [Validators.required, Validators.minLength(3)]],\n      terms: [false, Validators.requiredTrue],\n      animal: [null, Validators.required],\n      address: this.builder.group(\n        {\n          city: ['', Validators.required],\n          country: ['']\n        },\n        { validator: addressValidator }\n      )\n    });\n  }\n}\n```\n\nThe directive will show all errors for a form field automatically in two instances - on the field element blur and on form submit.\n\n## Inputs\n\n- `controlErrorsClass` - A custom classes that'll be added to the control error component and override custom classes from global config, a component that is added after the form field when an error needs to be displayed:\n\n```html\n\u003cinput class=\"form-control\" formControlName=\"city\" \n       placeholder=\"City\" controlErrorsClass=\"my-class other-class\" /\u003e\n       \n```\n\n- `controlCustomClass` - A custom classes that'll be added to the control if control has error.\n\n```html\n\u003cinput class=\"form-control\" formControlName=\"city\" \n       placeholder=\"City\" controlCustomClass=\"my-custom-class other-custom-class\" /\u003e\n       \n```\n\n- `controlErrorsTpl` - A custom error template to be used instead of the control error component's default view: \n\n```html\n\u003cform errorTailor\u003e\n  \u003cng-template let-error let-text=\"text\" #tpl\u003e {{ error | json }} {{ text }} \u003c/ng-template\u003e\n\n  \u003cdiv class=\"form-group\"\u003e\n    \u003cinput class=\"form-control\" ngModel required name=\"name\" [controlErrorsTpl]=\"tpl\" /\u003e\n  \u003c/div\u003e\n\n  \u003cbutton class=\"btn btn-success\"\u003eSubmit\u003c/button\u003e\n\u003c/form\u003e\n```\n\n- `controlErrorAnchor` - A custom `anchor` element for the control error component. The default anchor is the form field element:\n\n```html\n\u003cdiv class=\"form-check form-group\"\u003e\n  \u003cinput type=\"checkbox\" formControlName=\"terms\" id=\"check\" [controlErrorAnchor]=\"anchor\" /\u003e\n  \u003clabel class=\"form-check-label\" for=\"check\"\u003e\n    I agree to the terms and conditions\n  \u003c/label\u003e\n  \u003cng-template controlErrorAnchor #anchor=\"controlErrorAnchor\"\u003e\u003c/ng-template\u003e\n\u003c/div\u003e\n```\n\nThe custom `anchor` can also be added as a directive, in which case it'll act as the anchor for any nested form fields:\n\n```html\n\u003cdiv class=\"form-check form-group\" controlErrorAnchor\u003e\n  \u003cinput type=\"checkbox\" formControlName=\"terms\" id=\"check\" /\u003e\n  \u003clabel class=\"form-check-label\" for=\"check\"\u003e\n    I agree to the terms and conditions\n  \u003c/label\u003e\n\u003c/div\u003e\n```\n\n- `controlErrors` - Additional errors to use for the form field, that aren't specified in the config:\n\n```html\n\u003cinput class=\"form-control\" formControlName=\"country\" placeholder=\"Country\"\n       [controlErrors]=\"extraErrors\" /\u003e\n```\n\n- `controlErrorsIgnore` - A custom attribute on a form field element to skip instantiating of a control error component on it.\n\nOne typical case when to use it is radio buttons in the same radio group where it's enough to show only one error message and not all of them for each separate radio button.\n\n```html\n\u003cdiv class=\"form-group\"\u003e\n  Communication language: \u0026nbsp;\n  \u003cinput type=\"radio\" name=\"languages\" formControlName=\"languages\" \n         value=\"en\" id=\"en\"    [controlErrorAnchor]=\"anchorRadio\" /\u003e\n  \u003clabel class=\"form-radio-label\" for=\"en\"\u003eEnglish\u003c/label\u003e\n  \u003cinput type=\"radio\" name=\"languages\" formControlName=\"languages\" \n         value=\"de\" id=\"de\" controlErrorsIgnore /\u003e\n  \u003clabel class=\"form-radio-label\" for=\"de\"\u003eGerman\u003c/label\u003e\n  \u003cinput type=\"radio\" name=\"languages\" formControlName=\"languages\" \n         value=\"cs\" id=\"cs\" controlErrorsIgnore /\u003e\n  \u003clabel class=\"form-radio-label\" for=\"cs\"\u003eCzech\u003c/label\u003e\n\n  \u003cng-template controlErrorAnchor #anchorRadio=\"controlErrorAnchor\"\u003e\u003c/ng-template\u003e\n\u003c/div\u003e\n```\n\n- `controlErrorsOnAsync` - To modify the error display behavior to not show errors from async validators, set the following input:\n\n```html\n\u003cinput [controlErrorsOnAsync]=\"false\" formControlName=\"name\" /\u003e\n```\n\n- `controlErrorsOnBlur` - To modify the error display behavior to not show errors on blur, set the following input:\n\n```html\n\u003cinput [controlErrorsOnBlur]=\"false\" formControlName=\"name\" /\u003e\n```\n\n- To modify the error display behavior and show the errors on submission alone, we can disable both `controlErrorsOnBlur` and `controlErrorsOnAsync`:\n\n```html\n\u003cinput [controlErrorsOnBlur]=\"false\" [controlErrorsOnAsync]=\"false\" formControlName=\"name\" /\u003e\n```\n\n- `controlErrorsOnChange` - To modify the error display behavior to show/hide the errors on every change, set the following input:\n\n```html\n\u003cinput [controlErrorsOnChange]=\"true\" formControlName=\"name\" /\u003e\n```\n\n## Methods\n\n- `showError()` - Programmatic access to show a control error component (without a blur or a submit event). A validation error should still exist on that element. The key is the published `exportAs` reference of `errorTailor` to a directive instance of `ControlErrorsDirective` and calling its public method `showError()`.\n  \nSyntax as `@ViewChild('gdprErrorTailor', { static: true }) gdprErrorTailor: ControlErrorsDirective;` is used to get the reference and later call `this.gdprErrorTailor.showError()`.\n\n- `hideError()` - Programmatic access to hide an already shown control error component with the same logic as `showError()`, so for example: `this.gdprErrorTailor.hideError()`.\n\n```html\n\u003cinput type=\"checkbox\" formControlName=\"gdpr\" #gdprErrorTailor=\"errorTailor\" /\u003e\n```\n\n## CSS Styling\n\nThe library adds a `form-submitted` to the submitted form. You can use it to style your inputs:\n\n```css\n.form-submitted input.ng-invalid,\n.form-submitted select.ng-invalid {\n  border-color: #dc3545;\n}\n```\n\n## Config\n\n- `blurPredicate` - Elements that should listen the `focusout` event. The default predicate is:\n\n```ts\n{\n  blurPredicate(element) {\n    return element.tagName === 'INPUT' || element.tagName === 'SELECT';\n  }\n}\n```\n- `controlClassOnly` - Optional. If `true`, the error component won't be created and only the error class will be added to the control. Default is `false`. \n- `controlErrorsClass` - Optional. A custom classes that'll be added to the control error component. Can be override if you set attribute `controlErrorsClass` on control\n- `controlCustomClass` - Optional. A custom classes that'll be added to the control if control has error. Can be override if you set attribute `controlCustomClass` on control\n- `controlErrorComponent` - Optional. Allows changing the default component that is used to render \n  the errors. This component should implement the `ControlErrorComponent` interface. If you only need to\n  replace the error component's template, you may derive it from the default component, \n  `DefaultControlErrorComponent`, and provide the requisite HTML template.\n\n  A common example is when using Ionic forms where each form field is wrapped in an `ion-item` and errors\n  are best displayed as a sibling `ion-item` of the field. Example below shows how this can be done using \n  a custom control error component.\n\n  For example:\n\n```ts\n  // Custom error component that will replace the standard DefaultControlErrorComponent.\n  @Component({\n    standalone: true,\n    imports: [errorTailorImports],\n    template: `\n    \u003cion-item lines=\"none\" class=\"ion-text-wrap\" [class.hide-control]=\"hideError\"\u003e\n      \u003cion-label color=\"danger\" class=\"ion-no-margin ion-text-wrap\" stacked\u003e\n        {{ errorText }}\n      \u003c/ion-label\u003e\n    \u003c/ion-item\u003e\n    `\n  })\n  export class IonicControlErrorComponent extends DefaultControlErrorComponent {\n  }\n\nbootstrapApplication(AppComponent, {\n  providers: [\n    provideErrorTailorConfig({\n      errors: {\n        useValue: {\n          required: 'This field is required'\n        }\n      },\n      controlErrorComponent: IonicControlErrorComponent\n    })\n  ]\n})\n```\n\n- `controlErrorComponentAnchorFn` - Optional. A hook function that allows the error component's \n  HTML element to be repositioned in the DOM. By default error components are inserted at the\n  bottom of the field with error. If your UI layout dictates a different positioning \n  scheme, you may use this hook.\n\n  Since this error element can be placed anywhere in the DOM, it also has to be\n  removed when the error component is destroyed. To provide for this, this\n  function should return a callback that will then be invoked when the error component\n  is destroyed. You may use this to remove the error HTML element that you inserted\n  into the DOM yourself.\n\n  Example below shows how the Ionic specific error component is repositioned in the DOM\n  to suit Ionic's form layout. `hostElem` is the HTML element for the form control and\n  `errorElem` is the HTML element for the error component. \n  \n```ts\n  anchorIonicErrorComponent(hostElement: Element, errorElement: Element) {\n    hostElement.parentElement.insertAdjacentElement('afterend', errorElement);\n    return () =\u003e {\n      let errorNode = hostElement.parentElement.querySelector('custom-control-error');\n      if (errorNode) {\n        errorNode.remove();\n      }\n    };\n  }\n\nbootstrapApplication(AppComponent, {\n  providers: [\n    provideErrorTailorConfig({\n      errors: {\n        useValue: {\n          required: 'This field is required'\n        }\n      },\n      controlErrorComponent: IonicControlErrorComponent,\n      controlErrorComponentAnchorFn: anchorIonicErrorComponent\n    })\n  ]\n})\n\n  ```\n\n- `controlErrorsOn` - Optional. An object that allows the default behavior for showing the errors to be overridden. (each individual property in the object is optional, so it's possible to override only 1 setting)\n\n```ts\n{\n  controlErrorsOn: {\n    async: true,  // (default: true)\n    blur: true,   // (default: true)\n    change: true, // (default: false)\n  }\n}\n```\n\n## Recipes\n\n### I18n Example\n\nHere's how to support i18n:\n\n```ts\nimport { TranslocoService } from '@ngneat/transloco';\n\nbootstrapApplication(AppComponent, {\n  providers: [\n    provideErrorTailorConfig({\n      errors: {\n        useFactory(service: TranslocoService) {\n          return {\n            required: error =\u003e service.translate('errors.required')\n          };\n        },\n        deps: [TranslocoService]\n      }\n    })\n  ]\n})\n```\n\n### Control Error Style\n\nHere's a default style you can use for the error component:\n\n```css\n.control-error {\n  width: 100%;\n  margin-top: 0.25rem;\n  font-size: 12px;\n  color: #dc3545;\n}\n```\n","funding_links":["https://github.com/sponsors/ngneat","https://www.buymeacoffee.com/basalnetanel"],"categories":["Table of contents","TypeScript","Projects by main language"],"sub_categories":["Third Party Components","angular"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fngneat%2Ferror-tailor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fngneat%2Ferror-tailor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fngneat%2Ferror-tailor/lists"}