Skip to content

mkeller1992/ng-error-tooltips

Repository files navigation

ng-error-tooltips

npm version build status codecov

An Angular library for Reactive Forms and Signal Forms that displays tooltips on form inputs with errors.

The latest library version is compatible with Angular 21.
Starting with version 20.1.0, ng-error-tooltips is fully zoneless-compatible.

⚠️ Breaking Change (Signal Forms)

Starting with version 21.3.x, the ErrorTooltipSigDirective no longer reads the form field via [formField].

You must now explicitly pass the field using [errorTooltipField]:

<input
  [formField]="signalForm.name"
  ngErrorTooltipSig
  [errorTooltipField]="signalForm.name">

Demo

https://mkeller1992.github.io/ng-error-tooltips/


Install

npm i ng-error-tooltips

Setup

Standalone apps (ApplicationConfig / app.config.ts)

import { ApplicationConfig, provideZonelessChangeDetection, signal } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideErrorTooltips, type SupportedLanguage } from 'ng-error-tooltips';
import { validate } from '@angular/forms/signals';

import { routes } from './app.routes';

// Demo-language signal (in real apps: inject(LanguageService).currentLanguageCode)
export const demoLang = signal<SupportedLanguage>('de');

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideZonelessChangeDetection(),

    // lang is optional (defaults to 'de')
    // validate is only required for Signal Forms / CustomSigValidators
    provideErrorTooltips({ lang: demoLang, validate }),
  ],
};

Directives

import {
  ErrorTooltipDirective,
  ErrorTooltipSigDirective,
  ErrorTooltipSigFormDirective,
} from 'ng-error-tooltips';

Usage

Reactive Forms

Define a reactive form with validators in your TypeScript component.
For applications with language switching support, use the CustomValidatorsI18n variants.

import { Component, inject } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ErrorTooltipDirective, CustomValidators } from 'ng-error-tooltips';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss',
  imports: [FormsModule, ReactiveFormsModule, ErrorTooltipDirective],
})
export class AppComponent {
  private readonly formBuilder = inject(FormBuilder);

  formGroup: FormGroup = this.formBuilder.group({
    nameInput: new FormControl<string>('', {
      validators: [
        CustomValidators.required(),
        CustomValidators.minLength(3),
      ],
    }),
  });
}
<form [formGroup]="formGroup" (ngSubmit)="submit()">
  <input
    ngErrorTooltip
    formControlName="nameInput"
    placeholder="Enter your name*"
    type="text">

  <button type="submit">Submit</button>
</form>

Signal Forms

import { Component, inject, signal, viewChild } from '@angular/core';
import { form, FormField, submit } from '@angular/forms/signals';
import { CustomSigValidators, ErrorTooltipSigDirective, ErrorTooltipSigFormDirective } from 'ng-error-tooltips';

interface Employee {
  name: string;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  imports: [FormField, ErrorTooltipSigDirective, ErrorTooltipSigFormDirective],
})
export class AppComponent {
  private readonly v = inject(CustomSigValidators);

  readonly ttForm = viewChild(ErrorTooltipSigFormDirective);

  readonly employee = signal<Employee>({
    name: '',
  });

  readonly signalForm = form(this.employee, path => [
    this.v.requiredI18n(path.name),
    this.v.minLengthI18n(path.name, 3),
  ]);

  async submit() {
    // marks all fields touched + runs validation
    await submit(this.signalForm, async () => undefined);

    if (!this.signalForm().valid()) {
      // show all tooltips inside the container
      this.ttForm()?.showErrorTooltips();
    }
	else {
		this.ttForm()?.hideErrorTooltips();
	}
  }
}
<div ngErrorTooltipSigForm>
  <input
    [formField]="signalForm.name"
    ngErrorTooltipSig
    [errorTooltipField]="signalForm.name"    
    placeholder="Enter your name*"
    type="text">
</div>

<button type="button" (click)="submit()">Submit</button>

Two ways to pass additional properties

You can pass separate properties, such as placement:

<input
  ngErrorTooltip
  [placement]="'right'"
  formControlName="nameInput"
  placeholder="Enter your name*"
  type="text">

Alternatively, pass one or more properties via an ErrorTooltipOptions object:

import { ErrorTooltipOptions } from 'ng-error-tooltips';

tooltipOptions: ErrorTooltipOptions = {
  placement: 'right',
};
<input
  formControlName="ageInput"
  ngErrorTooltip
  [options]="tooltipOptions"
  placeholder="Enter your age*"
  type="number">

Note: Explicit inputs (e.g. [placement]) override the same values provided via [options].


Internationalisation (i18n)

Starting with version 21.1.0, ng-error-tooltips supports reactive multi-language error messages.

If you do nothing, the tooltip falls back to German (de) error messages.

To enable language switching, provide the current language as a Signal<'de' | 'fr' | 'en'> using provideErrorTooltips.

Whenever the language signal changes, all visible error tooltips update automatically.


Properties

name type default description
id string | number 0 A custom id that can be assigned to the tooltip
showFirstErrorOnly boolean false Whether the tooltip should only display the first error if multiple errors exist
placement Placement 'bottom-left' The position of the tooltip
zIndex number 1101 The z-index of the tooltip
tooltipClass string '' Additional CSS classes applied to the tooltip (::ng-deep)
shadow boolean true Whether the tooltip has a shadow
offset number 8 Offset of the tooltip relative to the element
width string '' Fixed width of the tooltip
maxWidth string '350px' Maximum width of the tooltip
pointerEvents "auto" | "none" 'auto' Whether the tooltip reacts to pointer events

Angular Jest unit tests

Mocking ErrorTooltipDirective (Reactive Forms)

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { ErrorTooltipDirective, MockErrorTooltipDirective } from 'ng-error-tooltips';
import { FormBuilder } from '@angular/forms';

describe('AppComponent', () => {
  let component: AppComponent;
  let fixture: ComponentFixture<AppComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [AppComponent],
      providers: [FormBuilder]
    })
    .overrideComponent(AppComponent, {
      remove: { imports: [ErrorTooltipDirective] },
      add: { imports: [MockErrorTooltipDirective] }
    })
    .compileComponents();

    fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });
});

Mocking ErrorTooltipSigDirective and ErrorTooltipSigFormDirective (Signal Forms)

import { ErrorTooltipSigDirective, MockErrorTooltipSigDirective } from 'ng-error-tooltips';

await TestBed.configureTestingModule({
  imports: [AppComponent],
})
  .overrideComponent(AppComponent, {
    remove: { imports: [ErrorTooltipSigDirective, ErrorTooltipSigFormDirective] },
    add: { imports: [MockErrorTooltipSigDirective, MockErrorTooltipSigFormDirective] }
  })
  .compileComponents();

About

An Angular library for reactive forms that displays tooltips on form inputs with errors, providing a user-friendly way to visualize validation messages.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors