• Stars
    star
    478
  • Rank 91,950 (Top 2 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 4 years ago
  • Updated 12 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

πŸ¦„ Making sure your tailor-made error solution is seamless!


Making sure your tailor-made error solution is seamless!

MIT commitizen PRs styled with prettier All Contributors ngneat spectator

The Error Tailor offers seamless handling of form errors, saving you the trouble of repeating the error boilerplate. It's fully customizable, so you can control when, where, and how each form field's errors are displayed. Sit back, relax, and let the Error Tailor do all the work!

Buy Me A Coffee

Getting Started

Run npm install @ngneat/error-tailor and add the imports to your application:

import { provideErrorTailorConfig } from '@ngneat/error-tailor';

bootstrapApplication(AppComponent, {
  providers: [
    provideErrorTailorConfig({
      errors: {
        useValue: {
          required: 'This field is required',
          minlength: ({ requiredLength, actualLength }) => 
                      `Expect ${requiredLength} but got ${actualLength}`,
          invalidAddress: error => `Address isn't valid`
        }
      }
    })
  ]
})

The errors config property takes a partial Provider, that should provide a HashMap<string | (err:any) => string> 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. Now the only thing you need to add is the errorTailor directive to your form:

<form [formGroup]="form" errorTailor>
  <div class="form-group">
    <input class="form-control" formControlName="name" placeholder="Name" />
  </div>

  <section formGroupName="address">
    <div class="form-group">
      <input class="form-control" formControlName="city" placeholder="City" />
    </div>

    <div class="form-group">
      <input class="form-control" formControlName="country" placeholder="Country" />
    </div>
  </section>

  <div class="form-group">
    <select formControlName="animal" class="form-control">
      <option *ngFor="let option of options; index as index" [ngValue]="option">
        {{ option.label }}
      </option>
    </select>
  </div>

  <button class="btn btn-success">Submit</button>
</form>
import { errorTailorImports } from '@ngneat/error-tailor';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [errorTailorImports, ReactiveFormsModule]
})
export class AppComponent {
  private builder = inject(FormBuilder);
  form: FormGroup;

  ngOnInit() {
    this.form = this.builder.group({
      name: ['', [Validators.required, Validators.minLength(3)]],
      terms: [false, Validators.requiredTrue],
      animal: [null, Validators.required],
      address: this.builder.group(
        {
          city: ['', Validators.required],
          country: ['']
        },
        { validator: addressValidator }
      )
    });
  }
}

The directive will show all errors for a form field automatically in two instances - on the field element blur and on form submit.

Inputs

  • 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:
<input class="form-control" formControlName="city" 
       placeholder="City" controlErrorsClass="my-class other-class" />
       
  • controlCustomClass - A custom classes that'll be added to the control if control has error.
<input class="form-control" formControlName="city" 
       placeholder="City" controlCustomClass="my-custom-class other-custom-class" />
       
  • controlErrorsTpl - A custom error template to be used instead of the control error component's default view:
<form errorTailor>
  <ng-template let-error let-text="text" #tpl> {{ error | json }} {{ text }} </ng-template>

  <div class="form-group">
    <input class="form-control" ngModel required name="name" [controlErrorsTpl]="tpl" />
  </div>

  <button class="btn btn-success">Submit</button>
</form>
  • controlErrorAnchor - A custom anchor element for the control error component. The default anchor is the form field element:
<div class="form-check form-group">
  <input type="checkbox" formControlName="terms" id="check" [controlErrorAnchor]="anchor" />
  <label class="form-check-label" for="check">
    I agree to the terms and conditions
  </label>
  <ng-template controlErrorAnchor #anchor="controlErrorAnchor"></ng-template>
</div>

The custom anchor can also be added as a directive, in which case it'll act as the anchor for any nested form fields:

<div class="form-check form-group" controlErrorAnchor>
  <input type="checkbox" formControlName="terms" id="check" />
  <label class="form-check-label" for="check">
    I agree to the terms and conditions
  </label>
</div>
  • controlErrors - Additional errors to use for the form field, that aren't specified in the config:
<input class="form-control" formControlName="country" placeholder="Country"
       [controlErrors]="extraErrors" />
  • controlErrorsIgnore - A custom attribute on a form field element to skip instantiating of a control error component on it.

One 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.

<div class="form-group">
  Communication language: &nbsp;
  <input type="radio" name="languages" formControlName="languages" 
         value="en" id="en"    [controlErrorAnchor]="anchorRadio" />
  <label class="form-radio-label" for="en">English</label>
  <input type="radio" name="languages" formControlName="languages" 
         value="de" id="de" controlErrorsIgnore />
  <label class="form-radio-label" for="de">German</label>
  <input type="radio" name="languages" formControlName="languages" 
         value="cs" id="cs" controlErrorsIgnore />
  <label class="form-radio-label" for="cs">Czech</label>

  <ng-template controlErrorAnchor #anchorRadio="controlErrorAnchor"></ng-template>
</div>
  • controlErrorsOnAsync - To modify the error display behavior to not show errors from async validators, set the following input:
<input [controlErrorsOnAsync]="false" formControlName="name" />
  • controlErrorsOnBlur - To modify the error display behavior to not show errors on blur, set the following input:
<input [controlErrorsOnBlur]="false" formControlName="name" />
  • To modify the error display behavior and show the errors on submission alone, we can disable both controlErrorsOnBlur and controlErrorsOnAsync:
<input [controlErrorsOnBlur]="false" [controlErrorsOnAsync]="false" formControlName="name" />
  • controlErrorsOnChange - To modify the error display behavior to show/hide the errors on every change, set the following input:
<input [controlErrorsOnChange]="true" formControlName="name" />

Methods

  • 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().

Syntax as @ViewChild('gdprErrorTailor', { static: true }) gdprErrorTailor: ControlErrorsDirective; is used to get the reference and later call this.gdprErrorTailor.showError().

  • hideError() - Programmatic access to hide an already shown control error component with the same logic as showError(), so for example: this.gdprErrorTailor.hideError().
<input type="checkbox" formControlName="gdpr" #gdprErrorTailor="errorTailor" />

CSS Styling

The library adds a form-submitted to the submitted form. You can use it to style your inputs:

.form-submitted input.ng-invalid,
.form-submitted select.ng-invalid {
  border-color: #dc3545;
}

Config

  • blurPredicate - Elements that should listen the focusout event. The default predicate is:
{
  blurPredicate(element) {
    return element.tagName === 'INPUT' || element.tagName === 'SELECT';
  }
}
  • controlErrorsClass - Optional. A custom classes that'll be added to the control error component. Can be override if you set attribute controlErrorsClass on control

  • 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

  • controlErrorComponent - Optional. Allows changing the default component that is used to render the errors. This component should implement the ControlErrorComponent interface. If you only need to replace the error component's template, you may derive it from the default component, DefaultControlErrorComponent, and provide the requisite HTML template.

    A common example is when using Ionic forms where each form field is wrapped in an ion-item and errors are best displayed as a sibling ion-item of the field. Example below shows how this can be done using a custom control error component.

    For example:

  // Custom error component that will replace the standard DefaultControlErrorComponent.
  @Component({
    standalone: true,
    imports: [errorTailorImports],
    template: `
    <ion-item lines="none" class="ion-text-wrap" [class.hide-control]="hideError">
      <ion-label color="danger" class="ion-no-margin ion-text-wrap" stacked>
        {{ errorText }}
      </ion-label>
    </ion-item>
    `
  })
  export class IonicControlErrorComponent extends DefaultControlErrorComponent {
  }

bootstrapApplication(AppComponent, {
  providers: [
    provideErrorTailorConfig({
      errors: {
        useValue: {
          required: 'This field is required'
        }
      },
      controlErrorComponent: IonicControlErrorComponent
    })
  ]
})
  • controlErrorComponentAnchorFn - Optional. A hook function that allows the error component's HTML element to be repositioned in the DOM. By default error components are inserted at the bottom of the field with error. If your UI layout dictates a different positioning scheme, you may use this hook.

    Since this error element can be placed anywhere in the DOM, it also has to be removed when the error component is destroyed. To provide for this, this function should return a callback that will then be invoked when the error component is destroyed. You may use this to remove the error HTML element that you inserted into the DOM yourself.

    Example below shows how the Ionic specific error component is repositioned in the DOM to suit Ionic's form layout. hostElem is the HTML element for the form control and errorElem is the HTML element for the error component.

  anchorIonicErrorComponent(hostElement: Element, errorElement: Element) {
    hostElement.parentElement.insertAdjacentElement('afterend', errorElement);
    return () => {
      let errorNode = hostElement.parentElement.querySelector('custom-control-error');
      if (errorNode) {
        errorNode.remove();
      }
    };
  }

bootstrapApplication(AppComponent, {
  providers: [
    provideErrorTailorConfig({
      errors: {
        useValue: {
          required: 'This field is required'
        }
      },
      controlErrorComponent: IonicControlErrorComponent,
      controlErrorComponentAnchorFn: anchorIonicErrorComponent
    })
  ]
})
  • 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)
{
  controlErrorsOn: {
    async: true,  // (default: true)
    blur: true,   // (default: true)
    change: true, // (default: false)
  }
}

Recipes

I18n Example

Here's how to support i18n:

import { TranslocoService } from '@ngneat/transloco';

bootstrapApplication(AppComponent, {
  providers: [
    provideErrorTailorConfig({
      errors: {
        useFactory(service: TranslocoService) {
          return {
            required: error => service.translate('errors.required')
          };
        },
        deps: [TranslocoService]
      }
    })
  ]
})

Control Error Style

Here's a default style you can use for the error component:

.control-error {
  width: 100%;
  margin-top: 0.25rem;
  font-size: 12px;
  color: #dc3545;
}

More Repositories

1

falso

All the Fake Data for All Your Real Needs πŸ™‚
TypeScript
3,098
star
2

spectator

🦊 πŸš€ A Powerful Tool to Simplify Your Angular Tests
TypeScript
2,068
star
3

transloco

πŸš€ 😍 The internationalization (i18n) library for Angular
TypeScript
1,856
star
4

until-destroy

🦊 RxJS operator that unsubscribe from observables on destroy
TypeScript
1,733
star
5

elf

πŸ§™β€β™€οΈ A Reactive Store with Magical Powers
TypeScript
1,527
star
6

content-loader

βšͺ️ SVG component to create placeholder loading, like Facebook cards loading.
TypeScript
733
star
7

hot-toast

🍞 Smoking hot Notifications for Angular. Lightweight, customizable and beautiful by default.
TypeScript
687
star
8

cashew

🐿 A flexible and straightforward library that caches HTTP requests in Angular
TypeScript
677
star
9

reactive-forms

(Angular Reactive) Forms with Benefits πŸ˜‰
TypeScript
609
star
10

tailwind

πŸ”₯ A schematic that adds Tailwind CSS to Angular applications
TypeScript
608
star
11

query

πŸš€ Powerful asynchronous state management, server-state utilities and data fetching for Angular Applications
TypeScript
555
star
12

forms-manager

πŸ¦„ The Foundation for Proper Form Management in Angular
TypeScript
517
star
13

helipopper

🚁 A Powerful Tooltip and Popover for Angular Applications
TypeScript
392
star
14

nx-serverless

πŸš€ The Ultimate Monorepo Starter for Node.js Serverless Applications
TypeScript
388
star
15

dialog

πŸ‘» A simple to use, highly customizable, and powerful modal for Angular Applications
TypeScript
371
star
16

hotkeys

πŸ€– A declarative library for handling hotkeys in Angular applications
TypeScript
325
star
17

edit-in-place

A flexible and unopinionated edit in place library for Angular applications
TypeScript
252
star
18

svg-icon

πŸ‘» A lightweight library that makes it easier to use SVG icons in your Angular Application
TypeScript
251
star
19

inspector

πŸ•΅οΈ An angular library that lets you inspect and change Angular component properties
TypeScript
218
star
20

dirty-check-forms

🐬Detect Unsaved Changes in Angular Forms
TypeScript
199
star
21

input-mask

🎭 @ngneat/input-mask is an angular library that creates an input mask
TypeScript
199
star
22

lib

πŸ€– Lets you focus on the stuff that matters
TypeScript
180
star
23

transloco-keys-manager

πŸ¦„ The Key to a Better Translation Experience
TypeScript
174
star
24

dag

🐠 An Angular service for managing directed acyclic graphs
TypeScript
153
star
25

bind-query-params

Sync URL Query Params with Angular Form Controls
TypeScript
147
star
26

from-event

🦊 ViewChild and FromEvent β€” a Match Made in Angular Heaven
TypeScript
137
star
27

overview

πŸ€– A collection of tools to make your Angular views more modular, scalable, and maintainable
TypeScript
118
star
28

aim

Angular Inline Module Schematics
TypeScript
97
star
29

cmdk

Fast, composable, unstyled command menu for Angular. Directly inspired from pacocoursey/cmdk
TypeScript
91
star
30

copy-to-clipboard

βœ‚οΈ Modern copy to clipboard. No Flash.
TypeScript
78
star
31

variabless

JS & CSS - A Match Made in Heaven πŸ’Ž
HTML
78
star
32

loadoff

🀯 When it comes to loaders, take a load off your mind...
TypeScript
78
star
33

effects

πŸͺ„ A framework-agnostic RxJS effects implementation
TypeScript
63
star
34

avvvatars

Beautifully crafted unique avatar placeholder for your next angular project.
TypeScript
46
star
35

react-rxjs

πŸ”Œ "Plug and play" for Observables in React Apps!
TypeScript
37
star
36

subscribe

Subscription Handling Directive
TypeScript
34
star
37

elf-ng-router-store

Bindings to connect Angular router to Elf
TypeScript
24
star
38

ng-standalone-nx

TypeScript
24
star
39

lit-file-generator

🎁 A lit generator for a component, directive, and controller.
JavaScript
19
star
40

storage

TypeScript
18
star
41

material-schematics

TypeScript
3
star
42

svg-icon-demo

TypeScript
1
star