1
votes

So I wanted to create a validator password and confirm password (two fields value should be the same).

This is reachable by create a crosse field validator which get these 2 formControls and compare their values by following this tutorial

export function fieldMatcherValidator(field1: string, field2: string): ValidatorFn {
    return (control: AbstractControl): {[key: string]: boolean} | null  => {
    const checkControl = control.get(field1);
    const confirmControl = control.get(field2);
    if (checkControl.pristine || confirmControl.pristine) {
        return null;
    }
    //.....more code compare values
}

then, I can set up a reactive form way in my component:

this.passwordForm = this.fb.group({
      password: new FormControl(undefined, [Validators.required]),
      confirmPassword: new FormControl(undefined, [Validators.required])
    }
    , {validator: fieldMatcherValidator('password', 'confirmPassword')}
    );

---- above code works perfect in reactive forms

My question is: how to write a Directive for the validator so that I can use it in a Template Driven Way also.

I tried to write a Directive like following, but this validator is not suppose to work on formControl, but should be on formGroup. The following directive can't grab the formGroup and it's controls, so I can't validate the formControls values.

@Directive({
  selector: '[cmFieldMatcher]',
  providers: [
    {provide: NG_VALIDATORS, useExisting: FieldMatcherDirective, multi: true}
  ]
})
export class FieldMatcherDirective implements Validator {
  @Input() field1: string;
  @Input() field2: string;

  validator: ValidatorFn;

  constructor() {
    this.validator = fieldMatcherValidator(this.field1, this.field2);
  }

  validate(control: AbstractControl) {
    return this.validator(control);
  }
}

When I use it in a template like this, I have no luck to get abstractControl instance...

<form #form="ngForm" cmFieldMatcher [field1]="'password2'" [field2]="'confirmPassword2'">
                <cm-form-field>
                    <cm-password-input  name="password2" ngModel #password2="ngModel" [label]="'Password'" required [strengthLevel]="passwordStrength" [messages]="passwordMessages">
                    </cm-password-input>
                    <cm-form-field-error  key="fieldMatch" [message]="'Password doesnt match'"></cm-form-field-error>
                </cm-form-field>

                <cm-form-field>
                    <cm-input [type]="'password'" name="confirmPassword2" ngModel #confirmPassword2="ngModel" required [label]="'Confirm Password'" [theme]="'primary'">
                    </cm-input>
                    <cm-form-field-error key="fieldMatch" [message]="'Password doesnt match'"></cm-form-field-error>
                </cm-form-field>
        </form>
1

1 Answers

2
votes

Change this line:

useExisting: forwardRef(() => FieldMatcherDirective)

And import from @angular/core

Also, it'll not work because you're getting your ValidatorFn in the constructor. The field1 and field2 inputs will always be undefined, because they get set before ngOnChanges hook is called. Instead, move that code to ngOnChanges like this:

ngOnChanges() {
    this.validator = fieldMatcherValidator(this.field1, this.field2);
  }

And change the validate() method to this:

validate(control: AbstractControl) {
    return this.validator ? this.validator(control) : null;
  }