Skip to content

Commit

Permalink
fix(input-mask): iban on-blur infinite validation loop (#499)
Browse files Browse the repository at this point in the history
  • Loading branch information
Igor Milly authored and GitHub Enterprise committed Jan 27, 2022
1 parent 74fbbe1 commit 0dff959
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 12 deletions.
29 changes: 28 additions & 1 deletion projects/ng-aquila/src/mask/iban-mask.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe('NxIbanMaskDirective', () => {
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [BasicIbanMaskComponent, FormIbanMaskComponent, FormWithInitalIbanMaskComponent],
declarations: [BasicIbanMaskComponent, FormIbanMaskComponent, FormWithInitalIbanMaskComponent, FormIbanOnBlurMaskComponent],
imports: [FormsModule, ReactiveFormsModule, NxMaskModule],
}).compileComponents();
}),
Expand Down Expand Up @@ -283,6 +283,20 @@ describe('NxIbanMaskDirective', () => {
expect(testInstance.testForm.get('maskInput')!.value).toBe('GD');
});

it('should set nxIbanInvalidCountryError error for non-existing country code (on-blur validation mode)', () => {
createTestComponent(FormIbanOnBlurMaskComponent);
const maskInput = testInstance.testForm.controls.maskInput;
const invalidIbanErrorKey = 'nxIbanInvalidCountryError';

assertInputValue(nativeElement, 'G', 'G');

expect(maskInput.getError(invalidIbanErrorKey)).toBeUndefined();

assertInputValue(nativeElement, 'GD', 'GD');

expect(maskInput.getError(invalidIbanErrorKey)).toBeTruthy();
});

it('should mark as invalid if iban is not valid', () => {
createTestComponent(FormIbanMaskComponent);

Expand Down Expand Up @@ -454,3 +468,16 @@ class FormWithInitalIbanMaskComponent extends IbanMaskTest {
maskInput: new FormControl('NL91 ABNA 0417 1643 00', {}),
});
}

@Component({
template: `
<form [formGroup]="testForm">
<input nxMask nxIbanMask formControlName="maskInput" [validateMask]="validateMask" />
</form>
`,
})
class FormIbanOnBlurMaskComponent extends IbanMaskTest {
testForm: FormGroup = new FormGroup({
maskInput: new FormControl('', { updateOn: 'blur' }),
});
}
28 changes: 17 additions & 11 deletions projects/ng-aquila/src/mask/iban-mask.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export const NX_IBAN_MASK_VALIDATORS: any = {
selector: 'input[nxIbanMask]',
exportAs: 'nxIbanMaskDirective',
providers: [NX_IBAN_MASK_VALIDATORS],
host: {
'(input)': '_countryCodeValid() && _touch()',
},
})
export class NxIbanMaskDirective implements OnInit, Validator {
private _countryCode!: string | null;
Expand Down Expand Up @@ -52,7 +55,7 @@ export class NxIbanMaskDirective implements OnInit, Validator {
private _setCountryCode(code: string): void {
code = code.toUpperCase();
if (code.length === 2 && this._countryCode !== code) {
if (this._countryCodeExists(code)) {
if (IBAN.countries[code]) {
this._countryCode = code;
this.maskDirective.setMask(this._getMask(this._countryCode));
} else {
Expand All @@ -71,13 +74,13 @@ export class NxIbanMaskDirective implements OnInit, Validator {
private _getMask(countryCode: string) {
// the countrySpecs of a country contain: countryCode ("DE"), length (22), structure ("F08F10")
// and an example belonging to each country
const countrySpecs = IBAN['countries'][countryCode];
const countrySpecs = IBAN.countries[countryCode];

// 'SS' for country code + '00' for IBAN checksum
let mask = 'SS00';

// split up after every third character
const characterDefs = countrySpecs['structure'].match(/.{1,3}/g);
const characterDefs = countrySpecs.structure.match(/.{1,3}/g);

characterDefs!.forEach((charDef: any) => {
const character = charDef[0];
Expand Down Expand Up @@ -107,15 +110,8 @@ export class NxIbanMaskDirective implements OnInit, Validator {
return mask;
}

private _countryCodeExists(countryCode: string): boolean {
return !!IBAN['countries'][countryCode];
}

private _validateFn() {
const enteredCountryCode = this._elementRef.nativeElement.value.substr(0, 2);
if (enteredCountryCode.length === 2 && !this._countryCodeExists(enteredCountryCode)) {
// immediately show error to user
this.maskDirective._touch();
if (this._countryCodeValid()) {
return { nxIbanInvalidCountryError: 'no valid country code' };
}
if (!IBAN.isValid(this.maskDirective.getUnmaskedValue())) {
Expand All @@ -128,4 +124,14 @@ export class NxIbanMaskDirective implements OnInit, Validator {
validate() {
return this.maskDirective.validateMask ? this._validateFn() : null;
}

_touch() {
this.maskDirective._touch();
}

private _countryCodeValid(): boolean {
const enteredCountryCode = this._elementRef.nativeElement.value.substr(0, 2);

return enteredCountryCode.length === 2 && !IBAN.countries[enteredCountryCode];
}
}

0 comments on commit 0dff959

Please sign in to comment.