Angular 2 form validating for repeat password

Angular

Angular Problem Overview


Please refer to this question regarding the Comparing fields in validator with Angular 2. Unfortunately Angular 2 changed a bit so that solution seems not working anymore. Here is my code:

import {IonicApp, Page, NavController, NavParams} from 'ionic/ionic'
import {Component} from 'angular2/core'
import {FORM_PROVIDERS, FormBuilder, Validators} from 'angular2/common'
import {ControlMessages} from '../../components/control-messages'
import {ValidationService} from '../../services/validation-service'

@Page({
  templateUrl: 'build/pages/account/register.html',
  directives: [ControlMessages]
})
export class RegisterPage {
  constructor(nav: NavController, private builder: FormBuilder) {
    this.nav = nav
    this.registerForm = this.builder.group({
      'name':     ['', Validators.required],
      'email':    ['', Validators.compose([Validators.required, ValidationService.emailValidator])],
      'password': ['', Validators.required],
      'repeat':   ['', this.customValidator]
      }
    )        
  }

  register() {    
    alert(this.registerForm.value.password)
  }

  private customValidator(control) {         
    //console.log(this.registerForm.value.password)
    //return {isEqual: control.value === this.registerForm.value.password}
    return true  
  }
}

My html:

<ion-content class="account">
  <ion-list padding>
    <form [ngFormModel]='registerForm' (submit)='register()'>
      <div class="centered">
        <img class="logo" src="img/logo.png" alt="">
      </div>
      <div class="spacer" style="height: 20px;"></div>
    
      <ion-input>
        <ion-label floating>Name</ion-label>
        <input type="text" ngControl='name' id='name'>
        <control-messages control="name"></control-messages>            
      </ion-input>
    
      <ion-input>
        <ion-label floating>Email</ion-label>
        <input type="email" ngControl='email' id='email'>
        <control-messages control="email"></control-messages>               
      </ion-input>

      <ion-input>
        <ion-label floating>Password</ion-label>
        <input type="password" ngControl='password' id='password' value="">
        <control-messages control="password"></control-messages>        
      </ion-input>

      <ion-input>
        <ion-label floating>Confirm Password</ion-label>
        <input type="password" ngControl='repeat' id='repeat'>
        <control-messages control="repeat"></control-messages>                
      </ion-input>

      <button class="calm" full type='submit' [disabled]='!registerForm.valid'>Register</button>
    
      <ion-item style="background-color:transparent;border:none;">
        <button class="text-button" clear item-right (click)="gotoLogin()">Have an account already, Login</button>
      </ion-item>
    </form>
  </ion-list>

</ion-content>

But unfortunately, I can't access the password value in my validating function. If I uncomment console.log(this.registerForm.value.password), then I get the following error message:

> EXCEPTION: TypeError: Cannot read property 'value' of undefined

Any idea? Thanks.

Angular Solutions


Solution 1 - Angular

I see several problems in your code. You try to use the this keyword in the validator function and this doesn't correspond to the instance of the component. It's because you reference the function when setting it as a validator function.

Moreover the value associated with a control can be reached in the value property.

That said, I think that the right way to validate your two fields together is to create a group and associate a validator in it:

import { FormBuilder, Validators } from '@angular/forms';
...
constructor(private fb: FormBuilder) { // <--- inject FormBuilder
  this.createForm();
}
createForm() {
  this.registerForm = this.fb.group({
    'name' : ['', Validators.required],
    'email': ['', [Validators.required, Validators.email] ],
    'passwords': this.fb.group({
      password: ['', Validators.required],
      repeat:   ['', Validators.required]
    }, {validator: this.matchValidator})
  });    
}

This way you will have access to all controls of the group and not only one and don't need anymore to use the this keyword... The group's form controls can be accessed using the controls property of the FormGroup. The FormGroup is provided when validation is triggered. For example:

matchValidator(group: FormGroup) {
  var valid = false;

  for (name in group.controls) {
    var val = group.controls[name].value
    (...)
  }

  if (valid) {
    return null;
  }

  return {
    mismatch: true
  };
}

See this anwer for more details:

Edit

To display the error, you can simply use the following:

<span *ngIf="!registerForm.passwords.valid" class="help-block text-danger">
  <div *ngIf="registerForm.passwords?.errors?.mismatch">
    The two passwords aren't the same
  </div>
</span>

Solution 2 - Angular

I've implemented a custom passwords match validator for Angular 4.

Besides checking if two values are matching, it also subscribes to changes from other control and re-validates when either of two controls is updated. Feel free to use it as a reference for your own implementation or just copy it directly.

Here's the link to the solution: https://gist.github.com/slavafomin/17ded0e723a7d3216fb3d8bf845c2f30.


And here I'm providing a copy of the code:

match-other-validator.ts

import {FormControl} from '@angular/forms';


export function matchOtherValidator (otherControlName: string) {

  let thisControl: FormControl;
  let otherControl: FormControl;

  return function matchOtherValidate (control: FormControl) {

    if (!control.parent) {
      return null;
    }

    // Initializing the validator.
    if (!thisControl) {
      thisControl = control;
      otherControl = control.parent.get(otherControlName) as FormControl;
      if (!otherControl) {
        throw new Error('matchOtherValidator(): other control is not found in parent group');
      }
      otherControl.valueChanges.subscribe(() => {
        thisControl.updateValueAndValidity();
      });
    }

    if (!otherControl) {
      return null;
    }

    if (otherControl.value !== thisControl.value) {
      return {
        matchOther: true
      };
    }

    return null;

  }

}

Usage

Here's how you can use it with reactive forms:

private constructForm () {
  this.form = this.formBuilder.group({
    email: ['', [
      Validators.required,
      Validators.email
    ]],
    password: ['', Validators.required],
    repeatPassword: ['', [
      Validators.required,
      matchOtherValidator('password')
    ]]
  });
}

More up-to-date validators could be found here: moebius-mlm/ng-validators.

Solution 3 - Angular

found much simpler solution. Not sure if this is the right way to do it but it works for me

<!-- PASSWORD -->
<ion-item [ngClass]="{'has-error': !signupForm.controls.password.valid && signupForm.controls.password.dirty}">
    <ion-input formControlName="password" type="password" placeholder="{{ 'SIGNUP.PASSWORD' | translate }}" [(ngModel)]="registerCredentials.password"></ion-input>
</ion-item>

<!-- VERIFY PASSWORD -->
<ion-item [ngClass]="{'has-error': !signupForm.controls.verify.valid && signupForm.controls.verify.dirty}">
       <ion-input formControlName="verify" [(ngModel)]="registerCredentials.verify" type="password" pattern="{{registerCredentials.password}}" placeholder="{{ 'SIGNUP.VERIFY' | translate }}"> </ion-input>
</ion-item>

See

pattern="{{registerCredentials.password}}"

Solution 4 - Angular

Angular 4.3.3 solution!

You can do it using: [formGroup], formGroupName, formControlName in html and new FormGroup, new FormControl and custom areEqual method in TS

reg.component.html

<div [formGroup]="userFormPassword">
  <div>
    <input formControlName="current_password" type="password" placeholder="Current Password">
  </div>

  <div formGroupName="passwords">
    <input formControlName="new_password" type="password" placeholder="New Password">
  </div>

  <div formGroupName="passwords">
    <input formControlName="repeat_new_password" type="password" class="form-control" placeholder="Repeat New Password">
    <div class="input-error" *ngIf="
          userFormPassword.controls['passwords'].errors &&
          userFormPassword.controls['passwords'].errors.areEqual &&
          userFormPassword.controls['passwords'].controls.repeat_new_password.touched &&
          userFormPassword.controls['passwords'].controls.new_password.touched
        ">PASSWORDS do not match
    </div>
  </div>
</div>

reg.component.ts

export class HomeHeaderSettingsModalComponent implements OnInit {
  userFormPassword: FormGroup;
  // ...
  
  static areEqual(c: AbstractControl): ValidationErrors | null {
    const keys: string[] = Object.keys(c.value);
    for (const i in keys) {
      if (i !== '0' && c.value[ keys[ +i - 1 ] ] !== c.value[ keys[ i ] ]) {
        return { areEqual: true };
      }
    }
  }

  ngOnInit() {
    this.userFormPassword = new FormGroup({
      'current_password': new FormControl(this.user.current_password, [
        Validators.required,
      ]),
      'passwords': new FormGroup({
        'new_password': new FormControl(this.user.new_password, [
          Validators.required
        ]),
        'repeat_new_password': new FormControl(this.user.repeat_new_password, [
          Validators.required
        ])
      }, HomeHeaderSettingsModalComponent.areEqual)
    });
  }
}

Result: result

Solution 5 - Angular

If you're using RC.5 and can't find ControlGroup, you can try using FormGroup. You may find out more from my answer:

Angular 2 RC.5 form validation for password repeat

Solution 6 - Angular

Summary

  • Trigger validation every time the value of the other control changes.
  • Unsubscribe to avoid memory leaks
  • returning {match: true} will allow us check if a given control has the error using myControl.hasError('match')

Implementation

import { AbstractControl, ValidatorFn } from '@angular/forms';
import { Subscription } from 'rxjs/Subscription';

export function matchOtherValidator(otherControlName: string): ValidatorFn {
    return (control: AbstractControl): { [key: string]: any } => {
        const otherControl: AbstractControl = control.root.get(otherControlName);

        if (otherControl) {
            const subscription: Subscription = otherControl
                .valueChanges
                .subscribe(() => {
                    control.updateValueAndValidity();
                    subscription.unsubscribe();
                });
        }

        return (otherControl && control.value !== otherControl.value) ? {match: true} : null;
    };
}

Example

this.registerForm = formBuilder.group({
            email: ['', [
                Validators.required, Validators.email
            ]],
            password: ['', [
                Validators.required, Validators.minLength(8)
            ]],
            confirmPassword: ['', [
                Validators.required, matchOtherValidator('password')
            ]]
        });

Solution 7 - Angular

Save the password into instance variable.

    password = new FormControl('', [Validators.required]);

Then use it in your form group.

        this.registrationForm = this.fb.group({
        'email': ['', [
            Validators.required,
            NGValidators.isEmail,
        ]
        ],
        'password': this.password,
        'password2': ['', [Validators.required, this.passwordMatch]]
    });

So the function looks like this.

 private passwordMatch() {
        let that = this;
        return (c: FormControl) =>
        {
            return (c.value == that.password.value) ? null : {'passwordMatch': {valid: false}};
        }
    }

I know it isnt the best solution, but its working!

Solution 8 - Angular

By using this library ng2-validation-manager you can easily do this:

this.form = new ValidationManager({
  'password'    : 'required|rangeLength:8,50',
  'repassword'  : 'required|equalTo:password'
});

Solution 9 - Angular

Also, as of angular 2 rc4 with forms 0.2.0 the markup and attribute calling out the group name used to encompass the grouped inputs is needed to prevent errors

<div formGroupName="passwords">group input fields here... </div>

Solution 10 - Angular

Well, i searched for answer on this topic and all were too big for my laziness so i did it like this. I think it gets the job done well.

> I used the ngModel to bind password and repeatPassword input and then i > have shown or hide the div with password comparison message with > [hidden] attribute in angular 2.

   <label for="usr">Password</label>
   <input placeholder="12345" id="password" type="text" class="form-control" 
   [(ngModel)]="password">
   <label for="usr">Repeat pasword</label> 
   <input placeholder="12345" type="text" class="form-control" 
   [(ngModel)]="repeatPassword">
   <div [hidden]="password == repeatPassword">Passwords do not match!</div>

Solution 11 - Angular

I found a solution that made me happier as far as consistency of code an error handling:

1st: Create a custom validation class with a static method which does the validation

This method should have a AbstractControl parameter which angular injects

Note that you will pass this in the ConfirmPassword control, so you need to call parent to get to the FormGroup. From there you call formGroup.get('myControl') and get the controls for password and confirm as you named them when you created the form group.

import {AbstractControl} from '@angular/forms';

export class PasswordValidation {

    static MatchPassword(AC: AbstractControl) {
       const formGroup = AC.parent;
       if (formGroup) {
            const passwordControl = formGroup.get('Password'); // to get value in input tag
            const confirmPasswordControl = formGroup.get('Confirm'); // to get value in input tag

            if (passwordControl && confirmPasswordControl) {
                const password = passwordControl.value;
                const confirmPassword = confirmPasswordControl.value;
                if (password !== confirmPassword) { 
                    return { matchPassword: true };
                } else {
                    return null;
                }
            }
       }

       return null;
    }
}

2nd: Use your Customer Validator just like you use angulars

this.registerForm = this.fb.group({ // <-- the parent FormGroup
                Email: ['', Validators.required ],
                Username: ['', Validators.required ],
                FirstName: ['', Validators.required ],
                Password: ['',
                                [
                                    Validators.required,
                                    Validators.minLength(6)
                                ]
                          ],
                Confirm: ['',
                                [
                                    Validators.required,
                                    PasswordValidation.MatchPassword
                                ]
                          ]
                });

Angular will then add the 'matchPassword': true to your Confirm controls errors exactly as it would add 'required' true when missing a value

Solution 12 - Angular

My solution for Angular 4.3.4, which doesn't require additional FormGroup:

  • register custom validator for repeatedPassword checking if passwords are the same
  • subscribe handler on password.valueChanges during form creation, and call .updateValueAndValidity() method on repeatedPassword

Here is some code:

form: FormGroup
passwordFieldName = 'password'
repeatedPasswordFieldName = 'repeatedPassword'

createForm() {
  this.form = this.formBuilder.group({
    login: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(255), Validators.email]],
    [passwordFieldName]: ['', [Validators.required, Validators.minLength(6), Validators.maxLength(255)]],
    [repeatedPasswordFieldName]: ['', [Validators.required, this.samePassword]]
  });

  this.form
    .get(passwordFieldName)
    .valueChanges.subscribe(() => {
      this.form
        .get(repeatedPasswordFieldName).updateValueAndValidity();
    })
}

samePassword(control: FormControl) {
  if (!control || !control.parent) {
    return null;
  }
  if (control.value !== control.parent.get(passwordFieldName).value) {
    return {'passwordMismatch': true}
  }
  return null;
}

Solution 13 - Angular

I just want to post my solution:

this.authorizationSettings = formBuilder.group({
      currentPassword: [null, Validators.compose([Validators.required, Validators.minLength(8)])],
      newPassword: [null, Validators.compose([Validators.required, Validators.minLength(8)])],
      repeatNewPassword: [null]
    });
    this.authorizationSettings.controls.newPassword.valueChanges.subscribe(data => {
      if (data) {
        data = data.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
      }
      this.authorizationSettings.controls.repeatNewPassword
        .clearValidators();
      this.authorizationSettings.controls.repeatNewPassword
        .setValidators(Validators.compose([Validators.required, Validators.pattern(data)]));
    });

We need to create form group first, then subscribe to first new password field then add validation to repeat field.

Solution 14 - Angular

Here's my way using Angular Validators

COMPONENT:

import { UserModel } from '../../settings/users/user.model';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { FormRequestModel } from '../Shared/form.model';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-add-user',
  templateUrl: './add-user.component.html',
  styleUrls: ['./add-user.component.scss']
})
export class AddUserComponent implements OnInit {

  passwordsForm: FormGroup;
  user: UserModel;
  constructor(private fb: FormBuilder) { }

  ngOnInit() {

      this.passwordsForm = this.fb.group({
        inputPassword: ['', Validators.compose([Validators.required, Validators.minLength(6), Validators.maxLength(50)])],
        inputPasswordAgain: ['']
      });
     
  }
}

HTML:

 <form class="form-horizontal" [formGroup]="passwordsForm" novalidate>
   <div class="form-group">
    <br/>
    <label for="inputPassword" class="col-sm-2 control-label">Password</label>
    <div class="col-sm-10">
      <input type="password" formControlName="inputPassword" class="form-control" id="inputPassword" placeholder="Password">
    </div>
  </div>
   <div class="alert alert-danger" *ngIf="!passwordsForm.controls['inputPassword'].valid && passwordsForm.controls['inputPassword'].touched">Password must contain at least 6 characters!!</div>
   

  <div class="form-group">
    <br/>
    <label for="inputPasswordAgain" class="col-sm-2 control-label">Password again</label>
    <div class="col-sm-10">
      <input type="password" formControlName="inputPasswordAgain" class="form-control" id="inputPasswordAgain" placeholder="Password again">
    </div>
  </div>

  <!-- Show div warning element if both inputs does not match the validation rules below -->

   <div class="alert alert-danger" *ngIf="passwordsForm.controls['inputPasswordAgain'].touched
   && passwordsForm.controls['inputPasswordAgain'].value !== passwordsForm.controls['inputPassword'].value">
   Both passwords must be equal!</div>

Solution 15 - Angular

If you want to create directive and use it then see this

Here is the complete directive code

import { Directive, Attribute  } from '@angular/core';
import { Validator,  NG_VALIDATORS } from '@angular/forms';

@Directive({
  selector: '[advs-compare]',
  providers: [{provide: NG_VALIDATORS, useExisting: CompareDirective, multi: true}]
})
export class CompareDirective implements Validator {

  constructor(@Attribute('advs-compare') public comparer: string){}

  validate(c: Control): {[key: string]: any} {
    let e = c.root.get(this.comparer);
    if(e && c.value !== e.value){
      return {"compare": true};
    }
    return null;
  }
}

For more information how to use it, see http://www.advancesharp.com/blog/1226/angular-5-email-compare-password-validation-different-ways

Solution 16 - Angular

This is the easiest way which I follow.I have removed code which are not relevant.

I think this will help you.

auth.component.ts,auth.component.html,auth.component.css

import { Component, OnInit, EventEmitter, Input, Output ,Directive, forwardRef, Attribute,OnChanges, SimpleChanges} from '@angular/core';
import { NG_VALIDATORS,Validator,Validators,AbstractControl,ValidatorFn  } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';

import { RegisterUser } from '../shared/models';

@Component({
  moduleId: module.id,
  selector: 'auth-page',
  styleUrls: ['./auth.component.css'],
  templateUrl: './auth.component.html'
})

export class AuthComponent implements OnInit {
  userRegisterModel: RegisterUser = new RegisterUser();
    
    constructor(private route: ActivatedRoute, private router: Router) {   }
  
  ngOnInit() { 
    this.userRegisterModel.LoginSource = 'M';//Manual,Facebook,Google
}

  userRegister() {
  console.log(this.userRegisterModel);
  }

}

.validation
{
  margin:0;
  padding-top: 1px;
  padding-bottom: 0px;
}
.validation .message
{
    font-size: 10px;
    color: #de1a16;
}

           <form (ngSubmit)="userRegister()" #authRegisterForm="ngForm" novalidate>

                            <div class="col-md-6 form-group" style="padding-right: 5px;padding-left: 0px;margin-bottom: 0;">
                                <label>First Name:</label>
                                <input type="text" class="form-control" required pattern="[a-zA-Z][a-zA-Z ]+" [(ngModel)]="userRegisterModel.firstName" name="firstName"
                                    #firstName="ngModel" placeholder="Your first name">
                                <div style="display: flex;">&nbsp;
                                    <div [hidden]="firstName.valid || firstName.pristine" class="validation">
                                        <div [hidden]="!firstName.hasError('required')" class="message">Name is required</div>
                                        <div [hidden]="!firstName.hasError('pattern')" class="message">Only alphabets allowed</div>
                                    </div>
                                </div>
                            </div>

                            <div class="col-md-6 form-group" style="padding-right: 5px;padding-left: 0px;margin-bottom: 0;">
                                <label>Last Name:</label>

                                <input type="text" class="form-control" required pattern="[a-zA-Z][a-zA-Z ]+" [(ngModel)]="userRegisterModel.lastName" name="lastName"
                                    #lastName="ngModel" placeholder="Your last name">


                                <div style="display: flex;">&nbsp;
                                    <div [hidden]="lastName.valid || lastName.pristine" class="validation">
                                        <div [hidden]="!lastName.hasError('required')" class="message">Name is required</div>
                                        <div [hidden]="!lastName.hasError('pattern')" class="message">Only alphabets allowed</div>
                                    </div>
                                </div>
                            </div>

                            <div class="col-md-12 form-group" style="padding-right: 5px;padding-left: 0px;margin-bottom: 0;">
                                <label>Email:</label>

                                <input type="text" class="form-control" required [(ngModel)]="userRegisterModel.email" name="email" pattern="^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$"
                                    #email="ngModel" placeholder="Your email">

                                <div style="display: flex;">&nbsp;
                                    <div [hidden]="email.valid || email.pristine" class="validation">
                                        <div [hidden]="!email.hasError('required')" class="message">Email is required</div>
                                        <div [hidden]="!email.hasError('pattern')" class="message">Email format should be
                                            <small>
                                                <b>[email protected]</b>
                                            </small>
                                        </div>
                                    </div>
                                </div>
                            </div>

                            <div class="col-md-12 form-group" style="padding-right: 5px;padding-left: 0px;margin-bottom: 0;">
                                <label>Password:</label>
                                <input type="password" class="form-control" required [(ngModel)]="userRegisterModel.password" name="password" #password="ngModel"
                                    minlength="6" placeholder="Your strong password" >

                                <div style="display: flex;">&nbsp;
                                    <div [hidden]="password.valid || password.pristine" class="validation">
                                        <div [hidden]="!password.hasError('minlength')" class="message">Password should be 6digit</div>
                                        <div [hidden]="!password.hasError('required')" class="message">Password is required</div>
                                    </div>
                                </div>
                            </div>

                            <div class="col-md-12 form-group" style="padding-right: 5px;padding-left: 0px;margin-bottom: 0;">
                                <label>Confirm Password:</label>
                                <input type="password" class="form-control" required validateEqual="password" [(ngModel)]="userRegisterModel.confirmPassword"
                                    name="confirmPassword" #confirmPassword="ngModel" placeholder="Confirm your password">

                                <div style="display: flex;">&nbsp;
                                    <div [hidden]="confirmPassword.valid || confirmPassword.pristine" class="validation">
                                        <div class="message">Passwords did not match</div>
                                    </div>
                                </div>
                            </div>

               
                            

                            <div class="col-md-12 form-group text-right" style="padding-right: 5px;padding-left: 0px;margin-bottom: 0;">
                                <button type="submit" class="btn btn-primary" [disabled]="!authRegisterForm.form.valid">
                                    Submit form
                                    <i class="icon-arrow-right14 position-right"></i>
                                </button>
                            </div>
                        </form>

registerUser.mocel.ts

export class RegisterUser {
    FirstName : number;
    LastName : string;
    Email: string;
    Password : string;  
  }      

password.match.directive.ts

import { Directive, forwardRef, Attribute } from '@angular/core';
import { NG_VALIDATORS,Validator,Validators,AbstractControl,ValidatorFn } from '@angular/forms';

@Directive({
    selector: '[validateEqual][formControlName],[validateEqual][formControl],[validateEqual][ngModel]',
    providers: [
        { provide: NG_VALIDATORS, useExisting: forwardRef(() => EqualValidator), multi: true }
    ]
})
export class EqualValidator implements Validator {
    constructor( @Attribute('validateEqual') public validateEqual: string) {}

    validate(c: AbstractControl): { [key: string]: any } {
        // self value (e.g. retype password)
        let v = c.value;

        // control value (e.g. password)
        let e = c.root.get(this.validateEqual);

        // value not equal
        if (e && v !== e.value) return {
            validateEqual: false
        }
        return null;
    }
}

app.module.ts

import { ModuleWithProviders, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';

//import { AppRoutingModule } from './app-routing.module';
//import { AppComponent } from './app.component';

//shared components
import {  EqualValidator} from './shared/password.match.directive';

@NgModule({
  declarations: [
   // AppComponent,
    //FooterComponent,
   // HeaderComponent,
   // LoginComponent,
   // LayoutComponent,
   // AuthComponent,
   // UserExistComponent,
  //  HomeComponent,
    EqualValidator
  ],
  imports: [
  // BrowserModule,    
  //  AppRoutingModule,
  //  SharedModule   
  ],
  providers: [
   // ApiService,
   // AuthGuard,
    //JwtService,  
   // UserService,
   // HomeAuthResolver,
   // NoAuthGuard,
   // SharedService
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Solution 17 - Angular

I don't think we need a custom validator for matching passwords, this can be easily achieved by using formname.controls['controlName'].value.

<input type="password" class="form-control" formControlName="password">
<div class="alert alert-danger" *ngIf="!userForm.controls['password'].valid && userForm.controls['password'].touched">
    Enter valid password between 7 and 14 characters.
</div>
<input type="password" class="form-control" formControlName="confPassword">
<div *ngIf="userForm.controls['confPassword'].touched">
    <div class="alert alert-danger" *ngIf="userForm.controls['confPassword'].value != userForm.controls['password'].value">
        Password do not match
    </div>
</div>

In fileName.component.ts the form control declared as:

'password':[null,  Validators.compose([Validators.required, Validators.minLength(7), Validators.maxLength(14))],
  'confPassword':[null, Validators.required]

Solution 18 - Angular

Responding to Zhou Hao person who raised the question of the debate I think that this is what you were looking for because it happened to me the same as you, he said that the variable was undefined and solve it like this:

  static comparePassword(control) {  
    try {
      control.parent.value.password;
      if (control.parent.value.password == control.value) {
        return null;
      } else {
          return { 'invalidValue': true }; 
      }
   } catch (e) {
      e.message;
   }
 }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBagusflyerView Question on Stackoverflow
Solution 1 - AngularThierry TemplierView Answer on Stackoverflow
Solution 2 - AngularSlava Fomin IIView Answer on Stackoverflow
Solution 3 - AngulardanggriantoView Answer on Stackoverflow
Solution 4 - Angularmixalbl4View Answer on Stackoverflow
Solution 5 - AngularChangView Answer on Stackoverflow
Solution 6 - AngularAndrei MihăeșView Answer on Stackoverflow
Solution 7 - AngularJan CizmarView Answer on Stackoverflow
Solution 8 - AngularSabri AziriView Answer on Stackoverflow
Solution 9 - AngularGregView Answer on Stackoverflow
Solution 10 - Angularmilan vilovView Answer on Stackoverflow
Solution 11 - AngularbgrahamView Answer on Stackoverflow
Solution 12 - AngularaknView Answer on Stackoverflow
Solution 13 - AngularKacper PolakView Answer on Stackoverflow
Solution 14 - AngularPawełView Answer on Stackoverflow
Solution 15 - AngularAli AdraviView Answer on Stackoverflow
Solution 16 - Angularisanka thalagalaView Answer on Stackoverflow
Solution 17 - AngularSai KrishnaView Answer on Stackoverflow
Solution 18 - AngularJuan Pablo Moreno MartínView Answer on Stackoverflow