Gereon99
Gereon99

Reputation: 807

My validator is telling 'invalid' even though it's actually not

Im currently making a text field, which shouldn't be empty and also shouldn't be allowed to start with a specific chain of characters. (lets say 'test')

So e.g. I want testxyz or to be invalid and anything else to be valid. I already got the RegExp: /^((?!(test)).)*$/gmi. I know something like abctest is also invalid, but that doesn't matter for this case.

The problem is that once I submit an invalid input every time I enter a string with an even (!) amount of characters the validator says that it's an invalid input (it prints 'Error 2'; you can see where that gets printed in the HTML) even though it's not!

So e.g. if I write test first it gives me an error saying it's invalid. Correct. If I then write ANY character, like a it doesn't give me an error. Also correct. But as soon as I write an even amount of characters, no matter what (!), like aa or wxyz it gives me an error where it shouldn't!

I can submit anyways it and everything is working, it's just the false 'interpretation' of the validator and I have no idea why he is doing that. Maybe someone can help me. I have added the code below.

HTML:

<h1 mat-dialog-title>{{data.dialogTitle}}</h1>
<div mat-dialog-content>
  <p class="mat-typography">{{data.dialogText}}</p>
</div>

<mat-form-field>
  <input matInput [(ngModel)]="name" placeholder="Enter your name?" [formControl]="InputControl">
  <mat-error *ngIf='InputControl.hasError("required")'>
    Error 1
  </mat-error>
  <mat-error *ngIf='InputControl.hasError("pattern")'>
    Error 2
  </mat-error>
</mat-form-field>

<mat-dialog-actions>
  <span style="flex: 1 1 auto"></span>
  <button mat-flat-button (click)="pressed()" class="mat-primary">OK</button>
</mat-dialog-actions>

TypeScript:

export class DialogComponent {

    regExp = /^((?!(test)).)*$/gmi;
    InputControl = new FormControl("", [Validators.required, Validators.pattern(this.regExp)])
    @Input() name:string

    constructor(public dialogRef: MatDialogRef<DialogComponent>,
                @Optional() @Inject(MAT_DIALOG_DATA) public data: any) { }

    pressed() {
        console.log(this.name.match(this.regExp));
        if(this.regExp.test(this.name)) {
            alert("valid");
            this.dialogRef.close(true);
        }
    }
}

Thanks in advance.

Upvotes: 2

Views: 892

Answers (2)

Gereon99
Gereon99

Reputation: 807

Okay, so apparently I found the mistake by myself. It had to do with the flags I used. gmi

I removed 'g' and now it works. I think it has something to do with the way RegExp objects work. When you use the 'g' flag the object keeps track of the lastIndex, where a match occured, so on subsequent matches it would start from there. If you remove the 'g'-flag it basically sets it to zero after a match and thus solves my problem. I'm not 100% sure about that however.

Upvotes: 0

Tomas
Tomas

Reputation: 3436

The root cause is in execution contexts. In your Validators.pattern function you are referring to it's parameter via this.regExp, but this function will not be executed in scope of current controller and as a result this will not be pointing to your controller but to some Angular internals of InputControl class in this case. So, saying simple, you lost this.

To quick and dirty solution change:

Validators.pattern(this.regExp)  

to:

Validators.pattern(/^((?!(test)).)*$/gmi)

And verify, if those help.

If yes, then if you still wish to store pattern in controller, you basically have to wrap your validator function in another controller function to enclose pattern in closure, that will be available to function in execution context:

getMyValidation(): (AbstractControl): ValidationErrors | null {
  let pattern = this.regExp;
  return Validators.pattern(pattern)
}

and pass it to form constructor like so:

InputControl = new FormControl("", [Validators.required, this.getMyValidation()])

Upvotes: 2

Related Questions