@ngspot/ngx-errors
Version:
<p align="center"> <img width="20%" height="20%" src="https://github.com/DmitryEfimenko/ngspot/blob/main/packages/ngx-errors/package/assets/logo.png?raw=true"> </p>
720 lines (702 loc) • 30.3 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, Injectable, inject, signal, Directive, Self, Optional, input, computed, effect, TemplateRef, ViewContainerRef, ChangeDetectorRef, isDevMode } from '@angular/core';
import { toObservable } from '@angular/core/rxjs-interop';
import { Subject, NEVER, merge, of, auditTime, asapScheduler, share, ReplaySubject, timer, switchMap, filter, take, pipe, Subscription, combineLatest } from 'rxjs';
import { distinctUntilChanged, filter as filter$1, switchMap as switchMap$1, map, tap } from 'rxjs/operators';
import * as i1 from '@angular/forms';
import { ControlContainer, FormGroup, FormArray, FormControl } from '@angular/forms';
/**
* Provides a way to add to available options for when to display an error for
* an invalid control. Options that come by default are
* `'touched'`, `'dirty'`, `'touchedAndDirty'`, `'formIsSubmitted'`.
*/
const CUSTOM_ERROR_STATE_MATCHERS = new InjectionToken('CUSTOM_ERROR_STATE_MATCHERS');
class ShowOnTouchedErrorStateMatcher {
isErrorState(control, form) {
return !!(control &&
control.invalid &&
(control.touched || (form && form.submitted)));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ShowOnTouchedErrorStateMatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ShowOnTouchedErrorStateMatcher, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ShowOnTouchedErrorStateMatcher, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class ShowOnDirtyErrorStateMatcher {
isErrorState(control, form) {
return !!(control &&
control.invalid &&
(control.dirty || (form && form.submitted)));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ShowOnDirtyErrorStateMatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ShowOnDirtyErrorStateMatcher, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ShowOnDirtyErrorStateMatcher, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class ShowOnTouchedAndDirtyErrorStateMatcher {
isErrorState(control, form) {
return !!(control &&
control.invalid &&
((control.dirty && control.touched) || (form && form.submitted)));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ShowOnTouchedAndDirtyErrorStateMatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ShowOnTouchedAndDirtyErrorStateMatcher, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ShowOnTouchedAndDirtyErrorStateMatcher, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class ShowOnSubmittedErrorStateMatcher {
isErrorState(control, form) {
return !!(control && control.invalid && form && form.submitted);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ShowOnSubmittedErrorStateMatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ShowOnSubmittedErrorStateMatcher, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ShowOnSubmittedErrorStateMatcher, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class ErrorStateMatchers {
constructor() {
this.showOnTouchedErrorStateMatcher = inject(ShowOnTouchedErrorStateMatcher);
this.showOnDirtyErrorStateMatcher = inject(ShowOnDirtyErrorStateMatcher);
this.showOnTouchedAndDirtyErrorStateMatcher = inject(ShowOnTouchedAndDirtyErrorStateMatcher);
this.showOnSubmittedErrorStateMatcher = inject(ShowOnSubmittedErrorStateMatcher);
this.customErrorStateMatchers = inject(CUSTOM_ERROR_STATE_MATCHERS, {
optional: true,
});
this.matchers = {
touched: this.showOnTouchedErrorStateMatcher,
dirty: this.showOnDirtyErrorStateMatcher,
touchedAndDirty: this.showOnTouchedAndDirtyErrorStateMatcher,
formIsSubmitted: this.showOnSubmittedErrorStateMatcher,
};
if (this.customErrorStateMatchers) {
this.matchers = { ...this.matchers, ...this.customErrorStateMatchers };
}
}
get(showWhen) {
return this.matchers[showWhen];
}
validKeys() {
return Object.keys(this.matchers);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ErrorStateMatchers, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ErrorStateMatchers, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ErrorStateMatchers, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [] });
/**
* Patches the method to first execute the provided function and then
* the original functionality
* @param obj Object with the method of interest
* @param methodName Method name to patch
* @param fn Function to execute before the original functionality
*/
function patchObjectMethodWith(obj, methodName, fn) {
const originalFn = obj[methodName].bind(obj);
function updatedFn(...args) {
fn(...args);
originalFn(...args);
}
obj[methodName] = updatedFn;
}
/**
* Extract a touched changed observable from an abstract control
* @param control AbstractControl
*
* @usage
* ```
* const formControl = new FormControl();
* const touchedChanged$ = extractTouchedChanges(formControl);
* ```
*/
function extractTouchedChanges(control) {
const methods = {
markAsTouched: true,
markAsUntouched: false,
};
return extractMethodsIntoObservable(control, methods).pipe(distinctUntilChanged());
}
/**
* Extract a dirty changed observable from an abstract control
* @param control AbstractControl
*
* @usage
* ```
* const formControl = new FormControl();
* const dirtyChanged$ = extractDirtyChanges(formControl);
* ```
*/
function extractDirtyChanges(control) {
const methods = {
markAsDirty: true,
markAsPristine: false,
};
return extractMethodsIntoObservable(control, methods).pipe(distinctUntilChanged());
}
function extractMethodsIntoObservable(control, methods) {
const changes$ = new Subject();
Object.keys(methods).forEach((methodName) => {
const emitValue = methods[methodName];
patchObjectMethodWith(control, methodName, () => {
changes$.next(emitValue);
});
});
return changes$.asObservable();
}
class NgxError extends Error {
constructor(message) {
super(`NgxError: ${message}`);
}
}
class ValueMustBeStringError extends NgxError {
constructor() {
super('Directive ngxError requires a string value');
}
}
class NoControlError extends NgxError {
constructor() {
super('Directive ngxErrors requires either control name or control instance');
}
}
class ControlInstanceError extends NgxError {
constructor() {
super('Control must be either a FormGroup, FormControl or FormArray');
}
}
class ControlNotFoundError extends NgxError {
constructor(name) {
super(`Control "${name}" could not be found`);
}
}
class ParentFormGroupNotFoundError extends NgxError {
constructor(name) {
super(`Can't search for control "${name}" because parent FormGroup is not found`);
}
}
class InvalidShowWhenError extends NgxError {
constructor(showWhen, keys) {
super(`Invalid showWhen value: ${showWhen}. Valid values are: ${keys.join(', ')}`);
}
}
class AllErrorsStateService {
constructor() {
this.state = signal(new Map());
}
registerControl(control, parentForm) {
const alreadyRegisteredControl = this.state().get(control);
if (alreadyRegisteredControl) {
alreadyRegisteredControl.registeredInstancesCount++;
return;
}
const watchedEvents$ = eventsTriggeringVisibilityChange$(control, parentForm);
this.state.update((map) => {
map.set(control, {
control,
parentForm,
watchedEvents$,
registeredInstancesCount: 1,
errors: signal({}),
});
return new Map(map);
});
}
unregisterControl(control) {
const alreadyRegisteredControl = this.state().get(control);
if (!alreadyRegisteredControl) {
return;
}
alreadyRegisteredControl.registeredInstancesCount--;
if (alreadyRegisteredControl.registeredInstancesCount === 0) {
this.state.update((map) => {
map.delete(control);
return new Map(map);
});
}
}
getControlState(control) {
return this.state().get(control);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: AllErrorsStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: AllErrorsStateService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: AllErrorsStateService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
function getErrorStateMatcher(errorStateMatchers, showWhen) {
const errorStateMatcher = errorStateMatchers.get(showWhen);
if (!errorStateMatcher) {
throw new InvalidShowWhenError(showWhen, errorStateMatchers.validKeys());
}
return errorStateMatcher;
}
function eventsTriggeringVisibilityChange$(control, form) {
const ngSubmit$ = form ? form.ngSubmit.asObservable() : NEVER;
const $ = merge(control.valueChanges, control.statusChanges, ngSubmit$, extractTouchedChanges(control), extractDirtyChanges(control), asyncBugWorkaround$(control), of(null)).pipe(
// using auditTime due to the fact that even though touch event
// might fire, the control.touched won't be updated at the time
// when ErrorStateMatcher check it
auditTime(0, asapScheduler), share({
connector: () => new ReplaySubject(1),
resetOnComplete: true,
resetOnError: true,
resetOnRefCountZero: true,
}));
return $;
}
/**
* control.statusChanges do not emit when there's async validator
* https://github.com/angular/angular/issues/41519
* ugly workaround:
*/
function asyncBugWorkaround$(control) {
let $ = NEVER;
if (control.asyncValidator && control.status === 'PENDING') {
$ = timer(0, 50).pipe(switchMap(() => of(control.status)), filter((x) => x !== 'PENDING'), take(1));
}
return $;
}
class NgxErrorsFormDirective {
constructor(ngForm, formGroupDirective) {
this.ngForm = ngForm;
this.formGroupDirective = formGroupDirective;
}
get form() {
return this.ngForm ?? this.formGroupDirective;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: NgxErrorsFormDirective, deps: [{ token: i1.NgForm, optional: true, self: true }, { token: i1.FormGroupDirective, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.7", type: NgxErrorsFormDirective, isStandalone: true, selector: "form", exportAs: ["ngxErrorsForm"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: NgxErrorsFormDirective, decorators: [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: 'form',
exportAs: 'ngxErrorsForm',
standalone: true,
}]
}], ctorParameters: () => [{ type: i1.NgForm, decorators: [{
type: Self
}, {
type: Optional
}] }, { type: i1.FormGroupDirective, decorators: [{
type: Self
}, {
type: Optional
}] }] });
class NgxErrorsBase {
constructor() {
this.errorsState = inject(AllErrorsStateService);
this.formDirective = inject(NgxErrorsFormDirective, {
optional: true,
skipSelf: true,
});
this.parentControlContainer = inject(ControlContainer, {
optional: true,
host: true,
skipSelf: true,
});
this.showWhen = input();
this.controlState = computed(() => {
const control = this.resolvedControl();
if (!control) {
return undefined;
}
const controlState = this.errorsState.getControlState(control);
return controlState;
});
this.registerResolvedControl = effect(() => {
const control = this.resolvedControl();
if (!control) {
return;
}
const form = this.formDirective?.form ?? null;
this.errorsState.registerControl(control, form);
}, { allowSignalWrites: true });
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: NgxErrorsBase, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "17.3.7", type: NgxErrorsBase, inputs: { showWhen: { classPropertyName: "showWhen", publicName: "showWhen", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: NgxErrorsBase, decorators: [{
type: Directive
}] });
const defaultConfig = {
showErrorsWhenInput: 'touched',
showMaxErrors: null,
};
const ERROR_CONFIGURATION = new InjectionToken('ERROR_CONFIGURATION', {
factory: () => {
return defaultConfig;
},
});
function mergeErrorsConfiguration(config) {
return { ...defaultConfig, ...config };
}
function provideNgxErrorsConfig(config = defaultConfig) {
return {
provide: ERROR_CONFIGURATION,
useValue: mergeErrorsConfiguration(config),
};
}
/**
* Marks the provided control as well as all of its children as dirty
* @param options to be passed into control.markAsDirty() call
*/
function markDescendantsAsDirty(control, options) {
control.markAsDirty(options);
if (control instanceof FormGroup || control instanceof FormArray) {
const controls = Object.keys(control.controls).map((controlName) => control.get(controlName));
controls.forEach((c) => {
c.markAsDirty(options);
if (c.controls) {
markDescendantsAsDirty(c, options);
}
});
}
}
function filterOutNullish() {
return pipe(filter$1((x) => x != null));
}
/* eslint-disable @angular-eslint/directive-selector */
let errorDirectiveId = 0;
/**
* Directive to provide a validation error for a specific error name.
* Used as a child of ngxErrors directive.
*
* Example:
* ```html
* <div [ngxErrors]="control">
* <div ngxError="required">This input is required</div>
* </div>
* ```
*/
class ErrorDirective {
constructor() {
this.subs = new Subscription();
this.config = inject(ERROR_CONFIGURATION);
this.errorStateMatchers = inject(ErrorStateMatchers);
this.errorsDirective = inject(NgxErrorsBase);
this.templateRef = inject(TemplateRef);
this.viewContainerRef = inject(ViewContainerRef);
this.cdr = inject(ChangeDetectorRef);
this.errorDirectiveId = ++errorDirectiveId;
this.errorName = input.required({ alias: 'ngxError' });
this.showWhen = input('', { alias: 'ngxErrorShowWhen' });
this.computedShowWhen = computed(() => {
const errorDirectiveShowWhen = this.showWhen();
if (errorDirectiveShowWhen) {
return errorDirectiveShowWhen;
}
const errorsDirectiveShowWhen = this.errorsDirective.showWhen();
if (errorsDirectiveShowWhen) {
return errorsDirectiveShowWhen;
}
if (this.config.showErrorsWhenInput === 'formIsSubmitted' &&
!this.errorsDirective.parentControlContainer) {
return 'touched';
}
return this.config.showErrorsWhenInput;
});
this.errorStateMatcher = computed(() => {
const showWhen = this.computedShowWhen();
return getErrorStateMatcher(this.errorStateMatchers, showWhen);
});
this.controlState$ = toObservable(this.errorsDirective.controlState).pipe(filterOutNullish());
/**
* Calculates whether the error could be shown based on the result of
* ErrorStateMatcher and whether there is an error for this particular errorName
* The calculation does not take into account config.showMaxErrors
*
* In addition, it observable produces a side-effect of updating NgxErrorsStateService
* with the information of whether this directive could be shown and a side-effect
* of updating err object in case it was mutated
*/
this.couldBeShown$ = combineLatest([
this.controlState$,
toObservable(this.errorName),
toObservable(this.errorStateMatcher),
]).pipe(switchMap$1(([controlState, errorName, errorStateMatcher]) => controlState.watchedEvents$.pipe(map(() => ({
controlState,
errorName,
errorStateMatcher,
})))), map(({ controlState, errorName, errorStateMatcher }) => {
const isErrorState = errorStateMatcher.isErrorState(controlState.control, controlState.parentForm);
const hasError = controlState.control.hasError(errorName);
const couldBeShown = isErrorState && hasError;
const prevCouldBeShown = controlState.errors()[this.errorDirectiveId];
return {
prevCouldBeShown,
couldBeShown,
errorName,
controlState,
hasError,
};
}), tap(({ controlState, errorName, prevCouldBeShown, couldBeShown, hasError, }) => {
if (prevCouldBeShown !== couldBeShown) {
controlState.errors.update((errors) => {
return { ...errors, [this.errorDirectiveId]: couldBeShown };
});
}
const err = controlState.control.getError(errorName);
const errorUpdated = hasError && JSON.stringify(this.err) !== JSON.stringify(err);
if (errorUpdated) {
this.err = err;
if (this.view) {
this.view.context.$implicit = this.err;
this.view.markForCheck();
}
}
}));
this.subscribeToCouldBeShown = this.subs.add(this.couldBeShown$.subscribe());
/**
* Determines whether the error is shown to the user based on
* the value of couldBeShown and the config.showMaxErrors.
* In addition, this reacts to the changes in visibility for all
* errors associated with the control
*/
this.isShown = computed(() => {
const controlState = this.errorsDirective.controlState();
if (!controlState) {
return false;
}
const errors = controlState.errors();
const couldBeShown = errors[this.errorDirectiveId];
if (!couldBeShown) {
return false;
}
const { showMaxErrors } = this.config;
if (!showMaxErrors) {
return true;
}
// get all errors for this control that are possibly visible,
// take directive ids associated with them, sort them
// and show only these with index <= to config.showMaxErrors
return Object.entries(errors)
.reduce((acc, curr) => {
const [id, couldBeShown] = curr;
if (couldBeShown) {
acc.push(Number(id));
}
return acc;
}, [])
.sort()
.filter((_, ix) => ix < showMaxErrors)
.includes(this.errorDirectiveId);
});
this.isShownEffect = effect(() => {
const isShown = this.isShown();
const control = this.errorsDirective.resolvedControl();
if (!control) {
return;
}
const prevHidden = this.hidden;
this.hidden = !isShown;
if (isShown) {
this.err = control.getError(this.errorName());
}
else {
this.err = {};
}
if (prevHidden !== this.hidden) {
this.toggleVisibility();
}
this.cdr.detectChanges();
});
this.hidden = true;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.err = {};
}
ngAfterViewInit() {
this.validateDirective();
}
ngOnDestroy() {
this.subs.unsubscribe();
}
toggleVisibility() {
if (this.hidden) {
if (this.view) {
this.view.destroy();
this.view = undefined;
}
}
else {
if (this.view) {
this.view.context.$implicit = this.err;
this.view.markForCheck();
}
else {
this.view = this.viewContainerRef.createEmbeddedView(this.templateRef, {
$implicit: this.err,
});
}
}
}
validateDirective() {
const errorName = this.errorName();
if (typeof errorName !== 'string' || errorName.trim() === '') {
throw new ValueMustBeStringError();
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ErrorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "17.3.7", type: ErrorDirective, isStandalone: true, selector: "[ngxError]", inputs: { errorName: { classPropertyName: "errorName", publicName: "ngxError", isSignal: true, isRequired: true, transformFunction: null }, showWhen: { classPropertyName: "showWhen", publicName: "ngxErrorShowWhen", isSignal: true, isRequired: false, transformFunction: null } }, exportAs: ["ngxError"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ErrorDirective, decorators: [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: '[ngxError]',
exportAs: 'ngxError',
standalone: true,
}]
}] });
/* eslint-disable @angular-eslint/directive-selector */
/**
* Directive to hook into the errors of a given control.
*
* Example:
*
* ```ts
* \@Component({
* template: `
* <div [ngxErrors]="myControl">
* <div ngxError="required">This input is required</div>
* </div>
* `
* })
* export class MyComponent {
* myControl = new FormControl('', Validators.required)
* }
* ```
* In case the `ngxErrors` directive is a child of a [formGroup], you can specify
* the control by the control name similarly how you'd do it with formControlName:
*
* ```ts
* \@Component({
* template: `
* <form [formGroup]="form">
* <div ngxErrors="firstName">
* <div ngxError="required">This input is required</div>
* </div>
* </form>
* `
* })
* export class MyComponent {
* form = this.fb.group({
* firstName: ['', Validators.required]
* });
* constructor(private fb: FormBuilder) {}
* }
* ```
*/
class ErrorsDirective extends NgxErrorsBase {
constructor() {
super(...arguments);
this.controlInput = input.required({
alias: 'ngxErrors',
});
this.resolvedControl = computed(() => {
const controlInput = this.controlInput();
// initialize directive only after control input was set AND after
// afterViewInit since parentFormGroupDirective might not be resolved
// before that
if (!this.afterViewInitComplete()) {
return;
}
if (!controlInput) {
throw new NoControlError();
}
if (typeof controlInput === 'string') {
if (!this.parentControlContainer) {
throw new ParentFormGroupNotFoundError(controlInput);
}
const control = this.parentControlContainer.control?.get(controlInput);
if (control == null) {
throw new ControlNotFoundError(controlInput);
}
return control;
}
if (!this.isAbstractControl(controlInput)) {
throw new ControlInstanceError();
}
return controlInput;
});
this.afterViewInitComplete = signal(false);
}
ngAfterViewInit() {
setTimeout(() => {
// Use of the setTimeout to ensure that the controlInput was surely set
// in all cases. In particular the edge-case where ngModelGroup
// declared via template driven forms results in the control being
// set later than ngAfterViewInit life-cycle hook is called
this.afterViewInitComplete.set(true);
}, 0);
}
isAbstractControl(control) {
return (control instanceof FormControl ||
control instanceof FormArray ||
control instanceof FormGroup);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ErrorsDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "17.3.7", type: ErrorsDirective, isStandalone: true, selector: "[ngxErrors]", inputs: { controlInput: { classPropertyName: "controlInput", publicName: "ngxErrors", isSignal: true, isRequired: true, transformFunction: null } }, providers: [{ provide: NgxErrorsBase, useExisting: ErrorsDirective }], exportAs: ["ngxErrors"], usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ErrorsDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngxErrors]',
exportAs: 'ngxErrors',
standalone: true,
providers: [{ provide: NgxErrorsBase, useExisting: ErrorsDirective }],
}]
}] });
const NGX_ERRORS_DECLARATIONS = [
ErrorsDirective,
ErrorDirective,
NgxErrorsFormDirective,
];
/**
* Makes it easy to trigger validation on the control, that depends on
* a value of a different control
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function dependentValidator(opts) {
let subscribed = false;
return (formControl) => {
const form = formControl.root;
const { watchControl, condition, validator } = opts;
const controlToWatch = watchControl(form);
if (!controlToWatch) {
if (isDevMode()) {
console.warn(`dependentValidator could not find specified watchControl`);
}
return null;
}
if (!subscribed) {
subscribed = true;
controlToWatch.valueChanges.subscribe(() => {
formControl.updateValueAndValidity();
});
}
if (condition === undefined || condition(controlToWatch.value)) {
const validatorFn = validator(controlToWatch.value);
return validatorFn(formControl);
}
return null;
};
}
/*
* Public API Surface of ngx-errors
*/
/**
* Generated bundle index. Do not edit.
*/
export { AllErrorsStateService, CUSTOM_ERROR_STATE_MATCHERS, ControlInstanceError, ControlNotFoundError, ERROR_CONFIGURATION, ErrorDirective, ErrorStateMatchers, ErrorsDirective, InvalidShowWhenError, NGX_ERRORS_DECLARATIONS, NgxError, NgxErrorsBase, NgxErrorsFormDirective, NoControlError, ParentFormGroupNotFoundError, ShowOnDirtyErrorStateMatcher, ShowOnSubmittedErrorStateMatcher, ShowOnTouchedAndDirtyErrorStateMatcher, ShowOnTouchedErrorStateMatcher, ValueMustBeStringError, dependentValidator, filterOutNullish, getErrorStateMatcher, markDescendantsAsDirty, provideNgxErrorsConfig };
//# sourceMappingURL=ngspot-ngx-errors.mjs.map