Skip to content

ngneat/error-tailor

Repository files navigation


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!

Getting Started

Run ng add @ngneat/error-tailor. This command updates the AppModule, and adds the ErrorTailorModule dependency:

@NgModule({
  declarations: [AppComponent],
  imports: [
    ReactiveFormsModule,
    ErrorTailorModule.forRoot({
      errors: {
        useValue() {
          required: error => `This field is required`,
          minlength: ({ requiredLength, actualLength }) => 
                       `Expect ${requiredLength} but got ${actualLength}`,
          invalidAddress: error => `Address isn't valid`
        },
      }
    })
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

The errors config property takes a partial Provider, that should provide an object containing the form errors. 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>
export class AppComponent {
  form: FormGroup;

  constructor(private builder: FormBuilder) {}

  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 class that'll be added to the control error component, 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"/>
  • 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="name" 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>
  • customErrors - Additional errors to use for the form field, that aren't specified in the config:
<input class="form-control" formControlName="city" placeholder="City" [customErrors]="serverErrors"/>

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';
  }
}
  • controlErrorsOnBlur - To modify the error display behavior and show the errors on submission alone, set the following input:
<input [controlErrorsOnBlur]="false" formControlName="name" />

Recipies

I18n Example

Here's how to support i18n:

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

@NgModule({
  declarations: [AppComponent],
  imports: [
    ErrorTailorModule.forRoot({
      errors: {
        useFactory(service: TranslocoService) {
          return {
            required: error => service.translate('errors.required'),
          };
        },
        deps: [TranslocoService]
      }
    })
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

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;
}

Contributors ✨

Thanks goes to these wonderful people (emoji key):


Netanel Basal

πŸ’» πŸ“– πŸ€” πŸš‡

Toni Villena

πŸ’» ⚠️

Inbal Sinai

πŸ“–

This project follows the all-contributors specification. Contributions of any kind welcome!

Icon made by Nhor Phai from www.flaticon.com