From 6813a9ebc350e7456637b0ac295a065c869f6cdb Mon Sep 17 00:00:00 2001 From: Nightapes Date: Fri, 3 Apr 2020 21:47:35 +0200 Subject: [PATCH] feat(directives): allow directive inputs to be changed on runtime --- .../creditcard/creditcard.directive.ts | 32 ++++++++-- src/components/email/email.directive.ts | 45 ++++++++++++-- src/components/equal-to/equal-to.directive.ts | 23 +++++++- src/components/password/password.directive.ts | 59 ++++++++++++++++++- .../universal/universal.directive.ts | 52 +++++++++++++++- 5 files changed, 195 insertions(+), 16 deletions(-) diff --git a/src/components/creditcard/creditcard.directive.ts b/src/components/creditcard/creditcard.directive.ts index 73701fd..8533c56 100644 --- a/src/components/creditcard/creditcard.directive.ts +++ b/src/components/creditcard/creditcard.directive.ts @@ -1,9 +1,16 @@ -import { Directive, Input, forwardRef, OnInit } from "@angular/core"; +import { + Directive, + Input, + forwardRef, + OnInit, + SimpleChanges +} from "@angular/core"; import { NG_VALIDATORS, Validator, ValidatorFn, - AbstractControl + AbstractControl, + ValidationErrors } from "@angular/forms"; import { CreditCardValidators } from "./creditcard-validators"; @@ -24,9 +31,14 @@ export class CreditCardValidatorDirective implements Validator, OnInit { @Input() creditCard = "all"; private validator: ValidatorFn; + private onChange: () => void; ngOnInit() { - switch (this.creditCard) { + this.setCreditcardValidator(this.creditCard); + } + + setCreditcardValidator(type: string) { + switch (type) { case "all": this.validator = CreditCardValidators.isCreditCard; break; @@ -57,8 +69,18 @@ export class CreditCardValidatorDirective implements Validator, OnInit { } } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - validate(c: AbstractControl): { [key: string]: any } { + ngOnChanges(changes: SimpleChanges): void { + if (changes["creditCard"]) { + this.setCreditcardValidator(changes["creditCard"].currentValue); + this.onChange(); + } + } + + validate(c: AbstractControl): ValidationErrors { return this.validator(c); } + + registerOnValidatorChange(fn: () => void): void { + this.onChange = fn; + } } diff --git a/src/components/email/email.directive.ts b/src/components/email/email.directive.ts index c0b5752..a686685 100644 --- a/src/components/email/email.directive.ts +++ b/src/components/email/email.directive.ts @@ -1,5 +1,12 @@ import { EmailOptions } from "./email-util"; -import { Directive, Input, forwardRef, OnInit } from "@angular/core"; +import { + Directive, + Input, + forwardRef, + OnInit, + OnChanges, + SimpleChanges +} from "@angular/core"; import { NG_VALIDATORS, Validator, @@ -21,13 +28,18 @@ import { EmailValidators } from "./email-validators"; } ] }) -export class EmailValidatorDirective implements Validator, OnInit { - @Input() email = "normal"; +export class EmailValidatorDirective implements Validator, OnInit, OnChanges { + @Input() email: "normal" | "simple" = "normal"; private validator: ValidatorFn; + private onChange: () => void; ngOnInit() { - switch (this.email) { + this.setValidator(this.email); + } + + setValidator(type: string) { + switch (type) { case "simple": this.validator = EmailValidators.simple; break; @@ -40,6 +52,17 @@ export class EmailValidatorDirective implements Validator, OnInit { } } + ngOnChanges(changes: SimpleChanges): void { + if (changes["email"]) { + this.setValidator(changes["email"].currentValue); + this.onChange(); + } + } + + registerOnValidatorChange?(fn: () => void): void { + this.onChange = fn; + } + validate(c: AbstractControl): ValidationErrors { return this.validator(c); } @@ -61,11 +84,25 @@ export class EmailSuggestValidatorDirective implements Validator, OnInit { @Input() emailSuggest: EmailOptions; private validator: ValidatorFn; + private onChange: () => void; ngOnInit() { this.validator = EmailValidators.suggest(this.emailSuggest); } + ngOnChanges(changes: SimpleChanges): void { + if (changes["emailSuggest"]) { + this.validator = EmailValidators.suggest( + changes["emailSuggest"].currentValue + ); + this.onChange(); + } + } + + registerOnValidatorChange?(fn: () => void): void { + this.onChange = fn; + } + validate(c: AbstractControl): ValidationErrors { return this.validator(c); } diff --git a/src/components/equal-to/equal-to.directive.ts b/src/components/equal-to/equal-to.directive.ts index 54b78b5..5b7d533 100644 --- a/src/components/equal-to/equal-to.directive.ts +++ b/src/components/equal-to/equal-to.directive.ts @@ -1,4 +1,11 @@ -import { Directive, forwardRef, Input, OnDestroy } from "@angular/core"; +import { + Directive, + forwardRef, + Input, + OnDestroy, + SimpleChanges, + OnChanges +} from "@angular/core"; import { AbstractControl, NG_VALIDATORS, @@ -20,16 +27,18 @@ import { delay } from "rxjs/operators"; } ] }) -export class EqualToDirective implements Validator, OnDestroy { +export class EqualToDirective implements Validator, OnDestroy, OnChanges { @Input() equalTo: string | AbstractControl; private subscription: Subscription; + private onChange: () => void; validate(c: AbstractControl): ValidationErrors | null { const otherControl = typeof this.equalTo === "string" ? c.parent.get(this.equalTo) : this.equalTo; + if (!this.subscription) { this.subscription = otherControl.valueChanges .pipe(delay(1)) @@ -43,4 +52,14 @@ export class EqualToDirective implements Validator, OnDestroy { ngOnDestroy(): void { this.subscription.unsubscribe(); } + + ngOnChanges(changes: SimpleChanges): void { + if (changes["equalTo"]) { + this.onChange(); + } + } + + registerOnValidatorChange(fn: () => void): void { + this.onChange = fn; + } } diff --git a/src/components/password/password.directive.ts b/src/components/password/password.directive.ts index b54df64..4b2d310 100644 --- a/src/components/password/password.directive.ts +++ b/src/components/password/password.directive.ts @@ -1,4 +1,11 @@ -import { Directive, Input, forwardRef, OnInit } from "@angular/core"; +import { + Directive, + Input, + forwardRef, + OnInit, + SimpleChanges, + OnChanges +} from "@angular/core"; import { NG_VALIDATORS, Validator, @@ -22,7 +29,8 @@ import { PasswordValidators } from "./password-validators"; } ] }) -export class PasswordValidatorDirective implements Validator, OnInit { +export class PasswordValidatorDirective + implements Validator, OnInit, OnChanges { @Input() repeatCharacter = 4; @Input() alphabeticalCharacter = 1; @Input() digitCharacter = 1; @@ -34,6 +42,7 @@ export class PasswordValidatorDirective implements Validator, OnInit { private digitCharacterValidator: ValidatorFn; private lowercaseCharacterValidator: ValidatorFn; private uppercaseCharacterValidator: ValidatorFn; + private onChange: () => void; ngOnInit() { this.repeatCharacterValidator = PasswordValidators.repeatCharacterRegexRule( @@ -53,6 +62,48 @@ export class PasswordValidatorDirective implements Validator, OnInit { ); } + ngOnChanges(changes: SimpleChanges): void { + let inputChanged = false; + if (changes["repeatCharacter"]) { + this.repeatCharacterValidator = PasswordValidators.repeatCharacterRegexRule( + changes["repeatCharacter"].currentValue + ); + inputChanged = true; + } + + if (changes["alphabeticalCharacter"]) { + this.alphabeticalCharacterValidator = PasswordValidators.alphabeticalCharacterRule( + changes["alphabeticalCharacter"].currentValue + ); + inputChanged = true; + } + + if (changes["digitCharacter"]) { + this.digitCharacterValidator = PasswordValidators.digitCharacterRule( + changes["digitCharacter"].currentValue + ); + inputChanged = true; + } + + if (changes["lowercaseCharacter"]) { + this.lowercaseCharacterValidator = PasswordValidators.lowercaseCharacterRule( + changes["lowercaseCharacter"].currentValue + ); + inputChanged = true; + } + + if (changes["uppercaseCharacter"]) { + this.uppercaseCharacterValidator = PasswordValidators.uppercaseCharacterRule( + changes["uppercaseCharacter"].currentValue + ); + inputChanged = true; + } + + if (inputChanged) { + this.onChange(); + } + } + validate(c: AbstractControl): ValidationErrors { const compose: ValidatorFn = Validators.compose([ this.repeatCharacterValidator, @@ -63,4 +114,8 @@ export class PasswordValidatorDirective implements Validator, OnInit { ]); return compose(c); } + + registerOnValidatorChange(fn: () => void): void { + this.onChange = fn; + } } diff --git a/src/components/universal/universal.directive.ts b/src/components/universal/universal.directive.ts index de034e5..2375a91 100644 --- a/src/components/universal/universal.directive.ts +++ b/src/components/universal/universal.directive.ts @@ -1,4 +1,11 @@ -import { Directive, forwardRef, Input, OnInit } from "@angular/core"; +import { + Directive, + forwardRef, + Input, + OnInit, + OnChanges, + SimpleChanges +} from "@angular/core"; import { AbstractControl, NG_VALIDATORS, @@ -98,6 +105,7 @@ export class IsInRangeValidatorDirective implements Validator, OnInit { @Input() maxValue: number; private validator: ValidatorFn; + private onChange: () => void; ngOnInit() { this.validator = UniversalValidators.isInRange( @@ -106,9 +114,23 @@ export class IsInRangeValidatorDirective implements Validator, OnInit { ); } + ngOnChanges(changes: SimpleChanges): void { + if (changes["minValue"] || changes["maxValue"]) { + this.validator = UniversalValidators.isInRange( + changes["minValue"].currentValue, + changes["maxValue"].currentValue + ); + this.onChange(); + } + } + validate(c: AbstractControl): ValidationErrors { return this.validator(c); } + + registerOnValidatorChange(fn: () => void): void { + this.onChange = fn; + } } @Directive({ @@ -123,18 +145,30 @@ export class IsInRangeValidatorDirective implements Validator, OnInit { } ] }) -export class MaxValidatorDirective implements Validator, OnInit { +export class MaxValidatorDirective implements Validator, OnInit, OnChanges { @Input() max: number; private validator: ValidatorFn; + private onChange: () => void; ngOnInit() { this.validator = UniversalValidators.max(this.max); } + ngOnChanges(changes: SimpleChanges): void { + if (changes["max"]) { + this.validator = UniversalValidators.max(changes["max"].currentValue); + this.onChange(); + } + } + validate(c: AbstractControl): ValidationErrors { return this.validator(c); } + + registerOnValidatorChange(fn: () => void): void { + this.onChange = fn; + } } @Directive({ @@ -149,16 +183,28 @@ export class MaxValidatorDirective implements Validator, OnInit { } ] }) -export class MinValidatorDirective implements Validator, OnInit { +export class MinValidatorDirective implements Validator, OnInit, OnChanges { @Input() min: number; private validator: ValidatorFn; + private onChange: () => void; ngOnInit() { this.validator = UniversalValidators.min(this.min); } + ngOnChanges(changes: SimpleChanges): void { + if (changes["min"]) { + this.validator = UniversalValidators.min(changes["min"].currentValue); + this.onChange(); + } + } + validate(c: AbstractControl): ValidationErrors { return this.validator(c); } + + registerOnValidatorChange(fn: () => void): void { + this.onChange = fn; + } }