From 221fda2d786d106d1a9497ac291bb6785a3aeb0d Mon Sep 17 00:00:00 2001 From: Nightapes Date: Mon, 20 Apr 2020 21:44:31 +0200 Subject: [PATCH] fix(directives): don't trigger validation changed on the first ngOnChanges call --- examples/package-lock.json | 21 ---- examples/package.json | 2 +- .../creditcard/creditcard.directive.ts | 31 ++--- src/components/email/email.directive.ts | 44 +++---- src/components/equal-to/equal-to.directive.ts | 40 ++----- src/components/password/password.directive.ts | 86 +++++--------- .../universal/universal.directive.ts | 107 ++++++++---------- 7 files changed, 111 insertions(+), 220 deletions(-) delete mode 100644 examples/package-lock.json diff --git a/examples/package-lock.json b/examples/package-lock.json deleted file mode 100644 index 837e4e7..0000000 --- a/examples/package-lock.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "examples", - "version": "0.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "ngx-highlightjs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ngx-highlightjs/-/ngx-highlightjs-2.1.1.tgz", - "integrity": "sha512-8LpImj51J8372Zztg/SHB5IznPVtujbW/Ner2KJOh1DjO7uph9jFt5AYdvtJrX9WeJ9Su+uuLlbjbSm2NgloKg==", - "requires": { - "tslib": "1.9.3" - } - }, - "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" - } - } -} diff --git a/examples/package.json b/examples/package.json index acdbef0..8667f0f 100644 --- a/examples/package.json +++ b/examples/package.json @@ -28,7 +28,7 @@ "core-js": "^2.4.1", "hammerjs": "^2.0.8", "ngx-highlightjs": "^2.1.1", - "ngx-validators": "5.2.0", + "ngx-validators": "file:../dist", "rxjs": "^6.2.2", "zone.js": "^0.8.5" }, diff --git a/src/components/creditcard/creditcard.directive.ts b/src/components/creditcard/creditcard.directive.ts index 8533c56..c89da73 100644 --- a/src/components/creditcard/creditcard.directive.ts +++ b/src/components/creditcard/creditcard.directive.ts @@ -1,33 +1,20 @@ -import { - Directive, - Input, - forwardRef, - OnInit, - SimpleChanges -} from "@angular/core"; -import { - NG_VALIDATORS, - Validator, - ValidatorFn, - AbstractControl, - ValidationErrors -} from "@angular/forms"; +import { Directive, Input, forwardRef, OnInit, SimpleChanges, OnChanges } from "@angular/core"; +import { NG_VALIDATORS, Validator, ValidatorFn, AbstractControl, ValidationErrors } from "@angular/forms"; import { CreditCardValidators } from "./creditcard-validators"; @Directive({ - selector: - "[creditCard][formControlName],[creditCard][formControl],[creditCard][ngModel]", + selector: "[creditCard][formControlName],[creditCard][formControl],[creditCard][ngModel]", providers: [ { provide: NG_VALIDATORS, // eslint-disable-next-line @typescript-eslint/no-use-before-define useExisting: forwardRef(() => CreditCardValidatorDirective), - multi: true - } - ] + multi: true, + }, + ], }) -export class CreditCardValidatorDirective implements Validator, OnInit { +export class CreditCardValidatorDirective implements Validator, OnInit, OnChanges { @Input() creditCard = "all"; private validator: ValidatorFn; @@ -70,8 +57,8 @@ export class CreditCardValidatorDirective implements Validator, OnInit { } ngOnChanges(changes: SimpleChanges): void { - if (changes["creditCard"]) { - this.setCreditcardValidator(changes["creditCard"].currentValue); + if (changes.creditCard && !changes.creditCard.isFirstChange()) { + this.setCreditcardValidator(changes.creditCard.currentValue); this.onChange(); } } diff --git a/src/components/email/email.directive.ts b/src/components/email/email.directive.ts index a686685..e641e71 100644 --- a/src/components/email/email.directive.ts +++ b/src/components/email/email.directive.ts @@ -1,19 +1,6 @@ import { EmailOptions } from "./email-util"; -import { - Directive, - Input, - forwardRef, - OnInit, - OnChanges, - SimpleChanges -} from "@angular/core"; -import { - NG_VALIDATORS, - Validator, - ValidatorFn, - AbstractControl, - ValidationErrors -} from "@angular/forms"; +import { Directive, Input, forwardRef, OnInit, OnChanges, SimpleChanges } from "@angular/core"; +import { NG_VALIDATORS, Validator, ValidatorFn, AbstractControl, ValidationErrors } from "@angular/forms"; import { EmailValidators } from "./email-validators"; @@ -24,9 +11,9 @@ import { EmailValidators } from "./email-validators"; provide: NG_VALIDATORS, // tslint:disable-next-line:no-forward-ref useExisting: forwardRef(() => EmailValidatorDirective), - multi: true - } - ] + multi: true, + }, + ], }) export class EmailValidatorDirective implements Validator, OnInit, OnChanges { @Input() email: "normal" | "simple" = "normal"; @@ -53,8 +40,8 @@ export class EmailValidatorDirective implements Validator, OnInit, OnChanges { } ngOnChanges(changes: SimpleChanges): void { - if (changes["email"]) { - this.setValidator(changes["email"].currentValue); + if (changes.email && !changes.email.isFirstChange()) { + this.setValidator(changes.email.currentValue); this.onChange(); } } @@ -69,18 +56,17 @@ export class EmailValidatorDirective implements Validator, OnInit, OnChanges { } @Directive({ - selector: - "[emailSuggest][formControlName],[emailSuggest][formControl],[emailSuggest][ngModel]", + selector: "[emailSuggest][formControlName],[emailSuggest][formControl],[emailSuggest][ngModel]", providers: [ { provide: NG_VALIDATORS, // tslint:disable-next-line:no-forward-ref useExisting: forwardRef(() => EmailSuggestValidatorDirective), - multi: true - } - ] + multi: true, + }, + ], }) -export class EmailSuggestValidatorDirective implements Validator, OnInit { +export class EmailSuggestValidatorDirective implements Validator, OnInit, OnChanges { @Input() emailSuggest: EmailOptions; private validator: ValidatorFn; @@ -91,10 +77,8 @@ export class EmailSuggestValidatorDirective implements Validator, OnInit { } ngOnChanges(changes: SimpleChanges): void { - if (changes["emailSuggest"]) { - this.validator = EmailValidators.suggest( - changes["emailSuggest"].currentValue - ); + if (changes.emailSuggest && !changes.emailSuggest.isFirstChange()) { + this.validator = EmailValidators.suggest(changes.emailSuggest.currentValue); this.onChange(); } } diff --git a/src/components/equal-to/equal-to.directive.ts b/src/components/equal-to/equal-to.directive.ts index 5b7d533..6253099 100644 --- a/src/components/equal-to/equal-to.directive.ts +++ b/src/components/equal-to/equal-to.directive.ts @@ -1,31 +1,18 @@ -import { - Directive, - forwardRef, - Input, - OnDestroy, - SimpleChanges, - OnChanges -} from "@angular/core"; -import { - AbstractControl, - NG_VALIDATORS, - ValidationErrors, - Validator -} from "@angular/forms"; +import { Directive, forwardRef, Input, OnDestroy, SimpleChanges, OnChanges } from "@angular/core"; +import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from "@angular/forms"; import { Subscription } from "rxjs"; import { delay } from "rxjs/operators"; @Directive({ - selector: - "[equalTo][ngModel], [equalTo][formControlName], [equalTo][formControl]", + selector: "[equalTo][ngModel], [equalTo][formControlName], [equalTo][formControl]", providers: [ { provide: NG_VALIDATORS, // tslint:disable-next-line:no-forward-ref useExisting: forwardRef(() => EqualToDirective), - multi: true - } - ] + multi: true, + }, + ], }) export class EqualToDirective implements Validator, OnDestroy, OnChanges { @Input() equalTo: string | AbstractControl; @@ -34,17 +21,12 @@ export class EqualToDirective implements Validator, OnDestroy, OnChanges { private onChange: () => void; validate(c: AbstractControl): ValidationErrors | null { - const otherControl = - typeof this.equalTo === "string" - ? c.parent.get(this.equalTo) - : this.equalTo; + const otherControl = typeof this.equalTo === "string" ? c.parent.get(this.equalTo) : this.equalTo; if (!this.subscription) { - this.subscription = otherControl.valueChanges - .pipe(delay(1)) - .subscribe(() => { - c.updateValueAndValidity(); - }); + this.subscription = otherControl.valueChanges.pipe(delay(1)).subscribe(() => { + c.updateValueAndValidity(); + }); } return c.value !== otherControl.value ? { notEqualTo: true } : null; } @@ -54,7 +36,7 @@ export class EqualToDirective implements Validator, OnDestroy, OnChanges { } ngOnChanges(changes: SimpleChanges): void { - if (changes["equalTo"]) { + if (changes.equalTo && !changes.equalTo.isFirstChange()) { this.onChange(); } } diff --git a/src/components/password/password.directive.ts b/src/components/password/password.directive.ts index 4b2d310..e79a99e 100644 --- a/src/components/password/password.directive.ts +++ b/src/components/password/password.directive.ts @@ -1,36 +1,20 @@ -import { - Directive, - Input, - forwardRef, - OnInit, - SimpleChanges, - OnChanges -} from "@angular/core"; -import { - NG_VALIDATORS, - Validator, - Validators, - ValidatorFn, - AbstractControl, - ValidationErrors -} from "@angular/forms"; +import { Directive, Input, forwardRef, OnInit, SimpleChanges, OnChanges } from "@angular/core"; +import { NG_VALIDATORS, Validator, Validators, ValidatorFn, AbstractControl, ValidationErrors } from "@angular/forms"; import { PasswordValidators } from "./password-validators"; @Directive({ - selector: - "[password][formControlName],[password][formControl],[password][ngModel]", + selector: "[password][formControlName],[password][formControl],[password][ngModel]", providers: [ { provide: NG_VALIDATORS, // tslint:disable-next-line:no-forward-ref useExisting: forwardRef(() => PasswordValidatorDirective), - multi: true - } - ] + multi: true, + }, + ], }) -export class PasswordValidatorDirective - implements Validator, OnInit, OnChanges { +export class PasswordValidatorDirective implements Validator, OnInit, OnChanges { @Input() repeatCharacter = 4; @Input() alphabeticalCharacter = 1; @Input() digitCharacter = 1; @@ -45,58 +29,44 @@ export class PasswordValidatorDirective private onChange: () => void; ngOnInit() { - this.repeatCharacterValidator = PasswordValidators.repeatCharacterRegexRule( - this.repeatCharacter - ); - this.alphabeticalCharacterValidator = PasswordValidators.alphabeticalCharacterRule( - this.alphabeticalCharacter - ); - this.digitCharacterValidator = PasswordValidators.digitCharacterRule( - this.digitCharacter - ); - this.lowercaseCharacterValidator = PasswordValidators.lowercaseCharacterRule( - this.lowercaseCharacter - ); - this.uppercaseCharacterValidator = PasswordValidators.uppercaseCharacterRule( - this.uppercaseCharacter - ); + this.repeatCharacterValidator = PasswordValidators.repeatCharacterRegexRule(this.repeatCharacter); + this.alphabeticalCharacterValidator = PasswordValidators.alphabeticalCharacterRule(this.alphabeticalCharacter); + this.digitCharacterValidator = PasswordValidators.digitCharacterRule(this.digitCharacter); + this.lowercaseCharacterValidator = PasswordValidators.lowercaseCharacterRule(this.lowercaseCharacter); + this.uppercaseCharacterValidator = PasswordValidators.uppercaseCharacterRule(this.uppercaseCharacter); } ngOnChanges(changes: SimpleChanges): void { let inputChanged = false; - if (changes["repeatCharacter"]) { - this.repeatCharacterValidator = PasswordValidators.repeatCharacterRegexRule( - changes["repeatCharacter"].currentValue - ); - inputChanged = true; + if (changes.repeatCharacter) { + this.repeatCharacterValidator = PasswordValidators.repeatCharacterRegexRule(changes.repeatCharacter.currentValue); + inputChanged = changes.repeatCharacter.isFirstChange() ? false : true; } - if (changes["alphabeticalCharacter"]) { + if (changes.alphabeticalCharacter) { this.alphabeticalCharacterValidator = PasswordValidators.alphabeticalCharacterRule( - changes["alphabeticalCharacter"].currentValue + changes.alphabeticalCharacter.currentValue ); - inputChanged = true; + inputChanged = changes.alphabeticalCharacter.isFirstChange() ? false : true; } - if (changes["digitCharacter"]) { - this.digitCharacterValidator = PasswordValidators.digitCharacterRule( - changes["digitCharacter"].currentValue - ); - inputChanged = true; + if (changes.digitCharacter) { + this.digitCharacterValidator = PasswordValidators.digitCharacterRule(changes.digitCharacter.currentValue); + inputChanged = changes.digitCharacter.isFirstChange() ? false : true; } - if (changes["lowercaseCharacter"]) { + if (changes.lowercaseCharacter) { this.lowercaseCharacterValidator = PasswordValidators.lowercaseCharacterRule( - changes["lowercaseCharacter"].currentValue + changes.lowercaseCharacter.currentValue ); - inputChanged = true; + inputChanged = changes.lowercaseCharacter.isFirstChange() ? false : true; } - if (changes["uppercaseCharacter"]) { + if (changes.uppercaseCharacter) { this.uppercaseCharacterValidator = PasswordValidators.uppercaseCharacterRule( - changes["uppercaseCharacter"].currentValue + changes.uppercaseCharacter.currentValue ); - inputChanged = true; + inputChanged = changes.uppercaseCharacter.isFirstChange() ? false : true; } if (inputChanged) { @@ -110,7 +80,7 @@ export class PasswordValidatorDirective this.digitCharacterValidator, this.alphabeticalCharacterValidator, this.lowercaseCharacterValidator, - this.uppercaseCharacterValidator + this.uppercaseCharacterValidator, ]); return compose(c); } diff --git a/src/components/universal/universal.directive.ts b/src/components/universal/universal.directive.ts index 2375a91..21d6fbf 100644 --- a/src/components/universal/universal.directive.ts +++ b/src/components/universal/universal.directive.ts @@ -1,32 +1,18 @@ -import { - Directive, - forwardRef, - Input, - OnInit, - OnChanges, - SimpleChanges -} from "@angular/core"; -import { - AbstractControl, - NG_VALIDATORS, - Validator, - ValidatorFn, - ValidationErrors -} from "@angular/forms"; +import { Directive, forwardRef, Input, OnInit, OnChanges, SimpleChanges } from "@angular/core"; +import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn, ValidationErrors } from "@angular/forms"; import { UniversalValidators } from "./universal-validators"; @Directive({ - selector: - "[noWhitespace][formControlName],[noWhitespace][formControl],[noWhitespace][ngModel]", + selector: "[noWhitespace][formControlName],[noWhitespace][formControl],[noWhitespace][ngModel]", providers: [ { provide: NG_VALIDATORS, // tslint:disable-next-line:no-forward-ref useExisting: forwardRef(() => WhiteSpaceValidatorDirective), - multi: true - } - ] + multi: true, + }, + ], }) export class WhiteSpaceValidatorDirective implements Validator, OnInit { private validator: ValidatorFn; @@ -41,16 +27,15 @@ export class WhiteSpaceValidatorDirective implements Validator, OnInit { } @Directive({ - selector: - "[noEmptyString][formControlName],[noEmptyString][formControl],[noEmptyString][ngModel]", + selector: "[noEmptyString][formControlName],[noEmptyString][formControl],[noEmptyString][ngModel]", providers: [ { provide: NG_VALIDATORS, // tslint:disable-next-line:no-forward-ref useExisting: forwardRef(() => EmptyStringValidatorDirective), - multi: true - } - ] + multi: true, + }, + ], }) export class EmptyStringValidatorDirective implements Validator, OnInit { private validator: ValidatorFn; @@ -65,16 +50,15 @@ export class EmptyStringValidatorDirective implements Validator, OnInit { } @Directive({ - selector: - "[isNumber][formControlName],[isNumber][formControl],[isNumber][ngModel]", + selector: "[isNumber][formControlName],[isNumber][formControl],[isNumber][ngModel]", providers: [ { provide: NG_VALIDATORS, // tslint:disable-next-line:no-forward-ref useExisting: forwardRef(() => IsNumberValidatorDirective), - multi: true - } - ] + multi: true, + }, + ], }) export class IsNumberValidatorDirective implements Validator, OnInit { private validator: ValidatorFn; @@ -89,18 +73,17 @@ export class IsNumberValidatorDirective implements Validator, OnInit { } @Directive({ - selector: - "[isInRange][formControlName],[isInRange][formControl],[isInRange][ngModel]", + selector: "[isInRange][formControlName],[isInRange][formControl],[isInRange][ngModel]", providers: [ { provide: NG_VALIDATORS, // tslint:disable-next-line:no-forward-ref useExisting: forwardRef(() => IsInRangeValidatorDirective), - multi: true - } - ] + multi: true, + }, + ], }) -export class IsInRangeValidatorDirective implements Validator, OnInit { +export class IsInRangeValidatorDirective implements Validator, OnInit, OnChanges { @Input() minValue: number; @Input() maxValue: number; @@ -108,18 +91,26 @@ export class IsInRangeValidatorDirective implements Validator, OnInit { private onChange: () => void; ngOnInit() { - this.validator = UniversalValidators.isInRange( - this.minValue, - this.maxValue - ); + this.validator = UniversalValidators.isInRange(this.minValue, this.maxValue); } ngOnChanges(changes: SimpleChanges): void { - if (changes["minValue"] || changes["maxValue"]) { - this.validator = UniversalValidators.isInRange( - changes["minValue"].currentValue, - changes["maxValue"].currentValue - ); + let minValue = this.minValue; + let maxValue = this.maxValue; + let changed = false; + + if (changes.minValue) { + minValue = changes.minValue.currentValue; + changed = changes.minValue.isFirstChange() ? false : true; + } + + if (changes.maxValue) { + maxValue = changes.maxValue.currentValue; + changed = changes.maxValue.isFirstChange() ? false : true; + } + + if (changed) { + this.validator = UniversalValidators.isInRange(minValue, maxValue); this.onChange(); } } @@ -134,16 +125,15 @@ export class IsInRangeValidatorDirective implements Validator, OnInit { } @Directive({ - selector: - "input[type=text][max][formControlName],input[type=text][max][formControl],input[type=text][max][ngModel]", + selector: "input[type=text][max][formControlName],input[type=text][max][formControl],input[type=text][max][ngModel]", providers: [ { provide: NG_VALIDATORS, // tslint:disable-next-line:no-forward-ref useExisting: forwardRef(() => MaxValidatorDirective), - multi: true - } - ] + multi: true, + }, + ], }) export class MaxValidatorDirective implements Validator, OnInit, OnChanges { @Input() max: number; @@ -156,8 +146,8 @@ export class MaxValidatorDirective implements Validator, OnInit, OnChanges { } ngOnChanges(changes: SimpleChanges): void { - if (changes["max"]) { - this.validator = UniversalValidators.max(changes["max"].currentValue); + if (changes.max && !changes.max.isFirstChange()) { + this.validator = UniversalValidators.max(changes.max.currentValue); this.onChange(); } } @@ -172,16 +162,15 @@ export class MaxValidatorDirective implements Validator, OnInit, OnChanges { } @Directive({ - selector: - "input[type=text][min][formControlName],input[type=text][min][formControl],input[type=text][min][ngModel]", + selector: "input[type=text][min][formControlName],input[type=text][min][formControl],input[type=text][min][ngModel]", providers: [ { provide: NG_VALIDATORS, // tslint:disable-next-line:no-forward-ref useExisting: forwardRef(() => MinValidatorDirective), - multi: true - } - ] + multi: true, + }, + ], }) export class MinValidatorDirective implements Validator, OnInit, OnChanges { @Input() min: number; @@ -194,8 +183,8 @@ export class MinValidatorDirective implements Validator, OnInit, OnChanges { } ngOnChanges(changes: SimpleChanges): void { - if (changes["min"]) { - this.validator = UniversalValidators.min(changes["min"].currentValue); + if (changes.min && !changes.min.isFirstChange()) { + this.validator = UniversalValidators.min(changes.min.currentValue); this.onChange(); } }