Skip to content

Commit 5aea6c8

Browse files
authored
feat: enable global errors to access directive Injector (#116)
Allow the Injector from the directive to be available in the global errors configuration. This enables retrieving custom component error messages from the global errors without needing to specify them at the component level each time.
1 parent 85d1a00 commit 5aea6c8

File tree

3 files changed

+34
-3
lines changed

3 files changed

+34
-3
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ bootstrapApplication(AppComponent, {
4848
<!-- prettier-ignore-end -->
4949

5050
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.
51+
This function runs inside the directive injector context.
52+
5153
Now the only thing you need to add is the `errorTailor` directive to your form:
5254

5355
```html

projects/ngneat/error-tailor/src/lib/control-error.directive.spec.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
import { Component, Type, ViewChild } from '@angular/core';
1+
import { Component, inject, Type, ViewChild } from '@angular/core';
22
import { fakeAsync, tick } from '@angular/core/testing';
33
import {
44
AbstractControl,
55
FormsModule,
6+
NgControl,
67
ReactiveFormsModule,
78
UntypedFormArray,
89
UntypedFormBuilder,
910
UntypedFormControl,
1011
ValidationErrors,
12+
ValidatorFn,
1113
Validators,
1214
} from '@angular/forms';
1315
import { ControlErrorsDirective, errorTailorImports, provideErrorTailorConfig } from '@ngneat/error-tailor';
@@ -473,6 +475,9 @@ describe('ControlErrorDirective', () => {
473475
});
474476

475477
describe('GlobalConfig', () => {
478+
const customValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null =>
479+
control.value === 'custom' ? { custom: control.value } : null;
480+
476481
@Component({
477482
selector: 'custom-error-form-group',
478483
standalone: true,
@@ -487,7 +492,7 @@ describe('ControlErrorDirective', () => {
487492
})
488493
class CustomErrorFormGroupComponent {
489494
form = this.builder.group({
490-
name: new UntypedFormControl('', [Validators.required]),
495+
name: new UntypedFormControl('', [Validators.required, customValidator]),
491496
});
492497
showName = true;
493498
constructor(private builder: UntypedFormBuilder) {}
@@ -511,6 +516,10 @@ describe('ControlErrorDirective', () => {
511516
errors: {
512517
useValue: {
513518
required: () => 'required error',
519+
custom: () => {
520+
const controlName = inject(NgControl).name;
521+
return `custom error for control ${controlName}`;
522+
},
514523
},
515524
},
516525
controlErrorsClass: ['global', 'config'],
@@ -624,5 +633,19 @@ describe('ControlErrorDirective', () => {
624633
expect(spectator.query(byText('required error'))).toBeFalsy();
625634
});
626635
});
636+
637+
describe('errors', () => {
638+
let spectator: Spectator<CustomErrorFormGroupComponent>;
639+
const createComponent = getCustomErrorComponentFactory(CustomErrorFormGroupComponent);
640+
641+
beforeEach(() => (spectator = createComponent()));
642+
643+
it('should be able to access directive injector', () => {
644+
const input = spectator.query<HTMLInputElement>(byPlaceholder('Name'));
645+
646+
spectator.typeInElement('custom', input);
647+
expect(spectator.query(byText('custom error for control name'))).toBeTruthy();
648+
});
649+
});
627650
});
628651
});

projects/ngneat/error-tailor/src/lib/control-error.directive.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import {
44
ElementRef,
55
EmbeddedViewRef,
66
Inject,
7+
Injector,
78
Input,
89
isDevMode,
910
OnDestroy,
1011
OnInit,
1112
Optional,
13+
runInInjectionContext,
1214
Self,
1315
TemplateRef,
1416
ViewContainerRef,
@@ -72,6 +74,7 @@ export class ControlErrorsDirective implements OnInit, OnDestroy {
7274
@Optional() private form: FormActionDirective,
7375
@Optional() @Self() private ngControl: NgControl,
7476
@Optional() @Self() private controlContainer: ControlContainer,
77+
private injector: Injector,
7578
) {
7679
this.host = elementRef.nativeElement as HTMLElement;
7780
this.submit$ = this.form ? this.form.submit$ : EMPTY;
@@ -193,7 +196,10 @@ export class ControlErrorsDirective implements OnInit, OnDestroy {
193196
return;
194197
}
195198

196-
const text = typeof getError === 'function' ? getError(controlErrors[firstKey]) : getError;
199+
const text =
200+
typeof getError === 'function'
201+
? runInInjectionContext(this.injector, () => getError(controlErrors[firstKey]))
202+
: getError;
197203
this.addCustomClass();
198204
this.setError(text, controlErrors);
199205
}

0 commit comments

Comments
 (0)