Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NgxFormsErrorAbstractComponent } from '@ngx/forms';

@Component({
selector: 'app-form-error',
template: `This is the error: {{ errors[0] }}`,
template: `
@if (errors.length) {
<ul class="form-error">
@for (error of errors; track error) {
<li>{{ error }}</li>
}
</ul>
}
`,
styleUrls: ['./error.component.scss'],
standalone: true,
imports: [CommonModule],
})
export class FormErrorComponent extends NgxFormsErrorAbstractComponent {}
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
<ng-container [formGroup]="form">
<p>Hello</p>
<!-- Structural syntax example -->
<p>Hello (structural)</p>
<input *ngxFormsErrors="'hello'" formControlName="hello" type="text" />

<p>World</p>
<input *ngxFormsErrors="'world'" formControlName="world" type="text" />
<!-- Attribute syntax example with per-control custom messages -->
<p>World (attribute w/ overrides)</p>
<input
formControlName="world"
type="text"
[ngxFormsErrors]="'world'"
[ngxFormsErrorsCustomErrorMessages]="{
required: 'Please fill out the world field',
minlength: 'World must be at least 3 characters',
pattern: 'World must start with a capital letter',
}"
/>

<p>Date</p>
<app-date-input *ngxFormsErrors="'date'" formControlName="date" />
<!-- Custom accessor component (attribute syntax) with override for required -->
<p>Date (custom component + override)</p>
<app-date-input
formControlName="date"
[ngxFormsErrors]="'date'"
[ngxFormsErrorsCustomErrorMessages]="{ required: 'Pick a date please' }"
/>
</ng-container>
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ export class FormAccessorComponent extends FormAccessor<any, any> {
initForm() {
return new FormGroup({
hello: new FormControl(null, [Validators.required]),
world: new FormControl(null, [Validators.required, Validators.minLength(3)]),
world: new FormControl(null, [
Validators.required,
Validators.minLength(3),
Validators.pattern(/^[A-Z].*/),
]),
date: new FormControl(null, Validators.required),
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ import { FormAccessorContainer, NgxFormsErrorsConfigurationToken } from '@ngx/fo
{
provide: NgxFormsErrorsConfigurationToken,
useValue: {
// Global (default) error messages. Individual controls can override these.
errors: {
required: 'This field is required',
minlength: 'Too short',
minlength: 'Value is too short (min 3 chars)',
pattern: 'Must start with an uppercase letter',
dependedDates: 'Something broke',
},
component: FormErrorComponent,
showWhen: 'touched',
// Showcase multiple errors rendering
show: 'all',
},
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,25 @@ Intended to be used in projects that require consistent error messages throughou

The error message is always rendered right below the element the `ngxErrors` directive is placed on.

---

## Why use `NgxFormsErrorsDirective`?

- **Consistency** – Define your application's error messages once at a root level.
- **Flexibility** – Render errors using the default DOM element or your own custom component.
- **Context-aware** – Override default error messages for specific form controls without changing the global configuration.
- **Integration-friendly** – Works with both _structural directive_ syntax (`*ngxFormsErrors`) and _attribute syntax_ (`[ngxFormsErrors]`).

---

## Configuration

To implement the `ngxErrors` directive, we have to provide the necessary configuration on root level and import the `NgxFormsErrorsDirective` where used.

A simple example is shown below.

```ts
// Root
// Root module or standalone bootstrap provide
providers: [
{
provide: NgxFormsErrorsConfigurationToken,
Expand All @@ -24,7 +35,7 @@ A simple example is shown below.
required: 'This is a required field.',
email: 'This field is not a valid email address.'
},
showWhen: 'touched',
showWhen: 'touched', // or 'dirty'
}
},
]
Expand All @@ -37,6 +48,13 @@ A simple example is shown below.
})
```

- `errors`: A mapping between Angular validation error keys and your display messages.
- `showWhen`: Determines when errors are displayed (`'touched'` or `'dirty'`).
- `component` _(optional)_: Provide a custom component to render errors instead of the default `<p>` element.
- `show` _(optional)_: Number of errors to display or `'all'` to show all.

---

## Basic implementation

By default, only two properties are required when setting up the `NgxFormsErrorsDirective`.
Expand All @@ -47,13 +65,28 @@ The `showWhen` property will determine when an error message becomes visible. Yo

Once configured, all we need to do is attach the directive where we wish to render the error. We suggest attaching this directly to the input or your custom input component.

You can attach the directive to an element in two ways:

**Structural syntax** (renders the input inside the directive’s view):

```html
<ng-container [formGroup]="form">
<p>Hello</p>
<input *ngxFormsErrors="'hello'" formControlName="hello" type="text" />
</ng-container>
```

**Attribute syntax** (applies directive directly to an existing element):

```html
<input formControlName="email" type="email" [ngxFormsErrors]="'email'" />
```

In both cases, `ngxFormsErrors` accepts either:

- A **string** key that matches the control name in the parent `FormGroup`.
- An **`AbstractControl`** instance directly.

The `ngxFormsErrors` directive allows for a string value that matches with the provided control in a `FormGroup`. Alternatively, you can also pass the `AbstractControl` directly.

By using this approach, when the control is invalid and in our case `touched`, the directive will render a `p` element with the `ngx-forms-error` class underneath the input.
Expand Down Expand Up @@ -89,12 +122,49 @@ The second Input is the `errorKeys` input, which provides us with an array of ke

On top of that, the `data` input provides us with the actual `ValidationErrors` on the control.

The `customErrorMessages` input will contain the per-control overrides if they were provided via `ngxFormsErrorsCustomErrorMessages`.

## Custom error messages per control

In addition to global messages configured at root level, you can now override them **per control** using the `ngxFormsErrorsCustomErrorMessages` input.

This is useful when a certain form field requires a different tone, extra context, or localized phrasing without affecting other fields.

```html
<input
formControlName="email"
type="text"
[ngxFormsErrors]="'email'"
[ngxFormsErrorsCustomErrorMessages]="{
required: 'Please provide your email address',
email: 'That doesn’t look like a valid email'
}"
/>
```

If a key is provided in `ngxFormsErrorsCustomErrorMessages`, it will take priority over the global configuration for that control. Any keys not overridden will still fall back to the global `errors` record.

This works for both the default `<p>` element output and custom components.

## Multiple errors

By default, the directive only renders a single error, the first one that gets provided in the validation errors object. If we wish to show more errors, we can provide the `show` property in the configuration.

We can either provide a specific number of errors we wish to see or provide the option `all` to see all errors.

```ts
{
provide: NgxFormsErrorsConfigurationToken,
useValue: {
errors,
showWhen: 'touched',
show: 'all' // or a number
}
}
```

---

## Example

{{ NgDocActions.demo("NgxerrorsDemoComponent", { expanded: true }) }}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ export class NgxFormsErrorAbstractComponent {
* The error object provided by the control
*/
@Input({ required: true }) public data: ValidationErrors;
/**
* An object containing custom error messages
*/
@Input() public customErrorMessages: Record<string, string>;
}
Loading