4

I have a a control which I want to validate:

  <mat-form-field class="example-full-width">
    <input matInput placeholder="Title " required [formControl]="titleFormControl">
    <mat-error *ngIf="titleFormControl.hasError('required')">
      Title is <strong>required</strong>
    </mat-error>
    <mat-error *ngIf="titleFormControl.hasError('duplicate')">
      Name already used
    </mat-error>
  </mat-form-field>

and in the component I have something like

titleFormControl = new FormControl('', {
    validators: [
      Validators.required,
      Validators.minLength(4)
    ],
  });

  ngOnInit(): void {
      this.titleFormControl.valueChanges
  .pipe(distinctUntilChanged()).subscribe(this.isNameDuplicate.bind(this))
  }

 isNameDuplicate(): void {
    const ret = this.data.findIndex((item, index) =>
      index !== this.selSheetIndex && item.id >= 0 && this.titleFormControl.value === item.title) >= 0;
    if (ret) {
      this.titleFormControl.setErrors({ 'duplicate': true });
      this.titleFormControl.markAsDirty(); // doesn't help neither :-/
    }
  }

If I change the text the mat-error is displayed immediately if the title is missing. When I change the content so isNameDuplicate() results in an error the control has an error 'duplicate'

I would expect that mat-error is displayed immediately but somehow this is not true. The evaluation of mat-error (for 'duplicate') is only triggered if I change the focus, e.g. change into another field. (while 'required' is displayed immediately)

Is there a way to trigger the evaluation manually so the 'duplicate' error mat-error is displayed as well immediately?

Note: I know I could do it with an error matcher but frankly said I have no clue how to apply a check with a given list. Therefore I am looking for a solution for this approach.

2 Answers 2

3

try with this (ValidatorFn):

titleFormControl = new FormControl('', {
validators: [
  Validators.required,
  Validators.minLength(4),
  this.isNameDuplicate.bind(this) // <----- Custom validator method call
],
});


 isNameDuplicate(control: AbstractControl): { [key: string]: boolean } | null {
 const ret = this.data.findIndex((item, index) => index !== this.selSheetIndex && item.id >= 0 && this.titleFormControl.value === item.title) >= 0;
 if (ret) {
  return { duplicate: true };
 }
}

in html

 <mat-error *ngIf="titleFormControl.hasError('duplicate')">
  Name already used
</mat-error>
Sign up to request clarification or add additional context in comments.

1 Comment

Oh thx...since I have two similar answers and yours is with the correct (working) syntax I'll accept yours. Inspecting the issue more closely I recognize just now that even with your approach the very first validation hit is not displayed. If I change focus and than back it works. No further focus changes required. Seems like I need to first loose focus on this field before the later validations are properly displayed in the control. No idea about the why...
1

Replace your validator in Async Validator of formControl and not in ngOnInit

titleFormControl = new FormControl('', [
      Validators.required,
      Validators.minLength(4),
      this.isNameDuplicate.bind(this)
    ]
  );
 isNameDuplicate: ValidatorFn = (control: FormControl): {[s: string]: boolean} {
    const ret = this.data.findIndex((item, index) =>
    index !== this.selSheetIndex && item.id >= 0 && control.value === item.title) >= 0;
    return ret ? {duplicate: true} : {};
  }

4 Comments

isNameDuplicate doesn't look as if it's doing anything async, it's just an array lookup.
So just replace the logic of AsyncvalidatorFn with ValidatorFn and just return the object in plain and not in Observable form
@Silente13: somehow the syntax has a problem. If I do it like you describe I get an error - 'control' is declared but its value is never read. - '=>' expected. And the second comment - I'm not sure what to do with it about replacing AsyncvalidatorFn with ValidatorFn. Does it mean your code is OK and that's just some background info?
@LeO I updated the answer with ValidatorFn and control is never read because the function was read the value from FormControl and not from binding. Fixed that!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.