Validador Angular2 que se basa en múltiples campos de formulario

118

¿Es posible crear un validador que pueda usar múltiples valores para decidir si mi campo es válido?

Por ejemplo, si el método de contacto preferido del cliente es el correo electrónico, el campo de correo electrónico debe ser obligatorio.

Gracias.


Actualizado con código de ejemplo ...


    import {Component, View} from 'angular2/angular2';
    import {FormBuilder, Validators, formDirectives, ControlGroup} from 'angular2/forms';

    @Component({
        selector: 'customer-basic',
        viewInjector: [FormBuilder]
    })
    @View({
        templateUrl: 'app/components/customerBasic/customerBasic.html',
        directives: [formDirectives]
    })
    export class CustomerBasic {
        customerForm: ControlGroup;

        constructor(builder: FormBuilder) {
            this.customerForm = builder.group({
                firstname: [''],
                lastname: [''],
                validateZip: ['yes'],
                zipcode: ['', this.zipCodeValidator] 
                // I only want to validate using the function below if the validateZip control is set to 'yes'
            });
        }

        zipCodeValidator(control) {
            if (!control.value.match(/\d\d\d\d\d(-\d\d\d\d)?/)) {
                return { invalidZipCode: true };
            }
        }

    }
Simón
fuente
Si. Y si nos muestra su código podríamos agregar una respuesta específica.
michelem
He añadido un ejemplo básico. En el código de muestra, ¿cómo puedo validar el código postal solo si el control validateZip anterior contiene 'sí'?
Simon
Simon, ¿por qué no promocionar la respuesta a tu pregunta?
superjos
6
Bien, para evitar que los futuros visitantes se sientan frustrados por esta pregunta, recomiendo encarecidamente utilizar este paquete de NPM: npmjs.com/package/ng2-validation . Tiene construcción en equaly equalTométodos y documentación bueno!
Michelangelo
2
Documentación angular: angular.io/guide/form-validation#cross-field-validation
ElliotSchmelliot

Respuestas:

147

Para reiterar los métodos que otros han publicado, esta es la forma en que he estado creando FormGroupvalidadores que no involucran a varios grupos.

Para este ejemplo, simplemente proporcione los nombres clave de los campos passwordy confirmPassword.

// Example use of FormBuilder, FormGroups, and FormControls
this.registrationForm = fb.group({
  dob: ['', Validators.required],
  email: ['', Validators.compose([Validators.required,  emailValidator])],
  password: ['', Validators.required],
  confirmPassword: ['', Validators.required],
  firstName: ['', Validators.required],
  lastName: ['', Validators.required]
}, {validator: matchingPasswords('password', 'confirmPassword')})

Para Validatorspoder tomar parámetros, deben devolver a functioncon FormGroupo FormControlcomo parámetro. En este caso, estoy validando un FormGroup.

function matchingPasswords(passwordKey: string, confirmPasswordKey: string) {
  return (group: FormGroup): {[key: string]: any} => {
    let password = group.controls[passwordKey];
    let confirmPassword = group.controls[confirmPasswordKey];

    if (password.value !== confirmPassword.value) {
      return {
        mismatchedPasswords: true
      };
    }
  }
}

Técnicamente, podría haber validado dos valores si hubiera conocido sus claves, pero prefiero nombrar mi Validatorsigual que el error que devolverán. La función podría modificarse para tomar un tercer parámetro que represente el nombre de clave del error devuelto.

Actualizado el 6 de diciembre de 2016 (v2.2.4)

Ejemplo completo: https://embed.plnkr.co/ukwCXm/

cyber_dave
fuente
@Dave << que no involucran a varios grupos >> ¿En realidad quiso decir << que involucran a varios grupos >>, o qué? Gracias
superjos
Esto no parece eliminar el marcado de alerta cuando las contraseñas coinciden con Angular 2 RC.1
datatype_void
"ControlGroups" no parece existir en 2.0. Usé 'FormGroup'
Stephen
@superjos Quise decir eso. Algunos desarrolladores optan por crear una FormGroupvalidación anidada para manejar múltiples campos en lugar de poner una Validatoren todo.
cyber_dave
1
¿Qué pasa si tenemos contraseña, confirmar contraseña y correo electrónico y confirmar correo electrónico? [{validator: matchingPasswords('password', 'confirmPassword')},{validator: matchingEmail('email', 'confirmemail')}] Intenté esto pero no funciona. Alguna sugerencia ? @Dave
Sharan Ainapurapu
51

La respuesta de Dave fue muy, muy útil. Sin embargo, una pequeña modificación podría ayudar a algunas personas.

En caso de que necesite agregar errores a los Controlcampos, puede mantener la construcción real del formulario y los validadores:

// Example use of FormBuilder, ControlGroups, and Controls
this.registrationForm= fb.group({
  dob: ['', Validators.required],
  email: ['', Validators.compose([Validators.required,  emailValidator])],
  password: ['', Validators.required],
  confirmPassword: ['', Validators.required],
  firstName: ['', Validators.required],
  lastName: ['', Validators.required]
}, {validator: matchingPasswords('password', 'confirmPassword')})

En lugar de establecer un error en el ControlGroup, hágalo en el campo real de la siguiente manera:

function matchingPasswords(passwordKey: string, passwordConfirmationKey: string) {
  return (group: ControlGroup) => {
    let passwordInput = group.controls[passwordKey];
    let passwordConfirmationInput = group.controls[passwordConfirmationKey];
    if (passwordInput.value !== passwordConfirmationInput.value) {
      return passwordConfirmationInput.setErrors({notEquivalent: true})
    }
  }
}
Luis Cruz
fuente
6
Úselo passwordConfirmationInput.setErrors(passwordConfirmationInput.validator(passwordConfirmationInput))en la elserama para que se actualice correctamente cuando un cambio en passwordInputhace que los datos sean válidos.
andraaspar
@andraaspar Lo intenté pero me salió el error TypeError: passwordConfirmationInput.validator is not a function. Es porque no creé explícitamente el FormControl con Validators.required. Dejé los validadores en blanco y en su lugar utilicé el atributo "requerido" en la entrada.
beardedlinuxgeek
6
Esto fue útil, pero noté que la documentación angular tiene un tipo de retorno de {[key: string]: any}, que setErrors(...)ya no regresa (¿ya?). También setErrors(...)sobrescribe cualquier error que ya esté presente, así que agregué al objeto de error actual como: let errors = formGroup.controls[passwordConfirmationKey].errors;y if(!errors) errors={};y errors['notEquivalent'] = true;yformGroup.controls[dateControlFirst].setErrors(errors);
Stephen
32

Al implementar validadores para múltiples campos de formulario, deberá asegurarse de que los validadores se vuelvan a evaluar cuando se actualice cada uno de los controles de formulario. La mayoría de los ejemplos no proporcionan una solución para tal escenario, pero esto es muy importante para la coherencia de los datos y el comportamiento correcto.

Consulte mi implementación de un validador personalizado para Angular 2, que tiene esto en cuenta: https://gist.github.com/slavafomin/17ded0e723a7d3216fb3d8bf845c2f30 .

Estoy usando otherControl.valueChanges.subscribe()para escuchar cambios en otro control y thisControl.updateValueAndValidity()para activar otra ronda de validación cuando se cambia otro control.


Estoy copiando un código a continuación para referencia futura:

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;

  }

}

Uso

Así es como puede usarlo con formas reactivas:

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

Se pueden encontrar validadores más actualizados aquí: moebius-mlm / ng-validators .

Slava Fomin II
fuente
¡¡Buena respuesta!! ¡He estado buscando una solución como esta durante horas! Considere un pequeño cambio: en lugar de perder "esta" referencia al devolver una función, devuelva una función como esta: return (control: FormControl) => {/ * código * /}
Vingtoft
Me alegro de haber podido ayudar. ¿Por qué necesita una referencia a thispara? En realidad, es bueno tener una función con nombre para fines de depuración.
Slava Fomin II
¿Actuación? funciona, pero en cuanto al rendimiento, no creo que sea una buena solución. Actualizar 'thisControl' cuando 'theOtherControl's value se cambia creará un bucle, ¿no?
nightElf91
¿Cuándo debería darse de baja? othercontrol.valuechanges.subscribeno se dio de baja en ninguna parte.
juana pu
@juanapu Presumiría que Angular terminará lo valueChangesobservable cuando otherControlse destruya, lo que hará que la suscripción también se cancele. Sin embargo, sus preocupaciones podrían ser válidas. Sugeriría depurar a fondo este código con la última versión de Angular usando varios casos de prueba. Por favor, informe si encuentra algún problema.
Slava Fomin II
23

Estoy usando Angular 2 RC.5 pero no pude encontrar ControlGroup, según la útil respuesta de Dave. Encontré que FormGroup funciona en su lugar. Así que hice algunas actualizaciones menores sobre los códigos de Dave y pensé en compartir con otros.

En su archivo de componentes, agregue una importación para FormGroup:

import {FormGroup} from "@angular/forms";

Defina sus entradas en caso de que necesite acceder al control de formulario directamente:

oldPassword = new FormControl("", Validators.required);
newPassword = new FormControl("", Validators.required);
newPasswordAgain = new FormControl("", Validators.required);

En su constructor, cree una instancia de su formulario:

this.form = fb.group({
  "oldPassword": this.oldPassword,
  "newPassword": this.newPassword,
  "newPasswordAgain": this.newPasswordAgain
}, {validator: this.matchingPasswords('newPassword', 'newPasswordAgain')});

Agregue la función matchingPasswords en su clase:

matchingPasswords(passwordKey: string, passwordConfirmationKey: string) {
  return (group: FormGroup) => {
    let passwordInput = group.controls[passwordKey];
    let passwordConfirmationInput = group.controls[passwordConfirmationKey];
    if (passwordInput.value !== passwordConfirmationInput.value) {
      return passwordConfirmationInput.setErrors({notEquivalent: true})
    }
  }
}

Espero que esto ayude a aquellos que están usando RC.5. Tenga en cuenta que todavía no he probado en RC.6.

Chang
fuente
@Sam ¿Cambiaste algo para que funcione con la versión final? no funciona para mí .. Dice: Argumento de tipo '{validador: (grupo: FormGroup) => void; } 'no se puede asignar al parámetro de tipo' ValidatorFn '.
xtof
No, no necesitaba cambiar nada; para mí, el código de muestra anterior funcionó con Angular2 final. ¿Está utilizando el código exacto como el anterior?
Sam
Buena solución @Chang. Si cambia su contraseña después de completar su contraseña de confirmación. La validación no funciona. Puedes probarif (passwordInput.value !== passwordConfirmationInput.value) { return passwordConfirmationInput.setErrors({ notEquivalent: true }); } else { return passwordConfirmationInput.setErrors(null); }
Mario Shtika
16

Mucha excavación en fuente angular, pero he encontrado una mejor manera.

constructor(...) {
    this.formGroup = builder.group({
        first_name:        ['', Validators.required],
        matching_password: builder.group({
            password: ['', Validators.required],
            confirm:  ['', Validators.required]
        }, this.matchPassword)
    });

    // expose easy access to passworGroup to html
    this.passwordGroup = this.formGroup.controls.matching_password;
}

matchPassword(group): any {
    let password = group.controls.password;
    let confirm = group.controls.confirm;

    // Don't kick in until user touches both fields   
    if (password.pristine || confirm.pristine) {
      return null;
    }

    // Mark group as touched so we can add invalid class easily
    group.markAsTouched();

    if (password.value === confirm.value) {
      return null;
    }

    return {
      isValid: false
    };
}

Parte HTML para el grupo de contraseñas

<div ng-control-group="matching_password" [class.invalid]="passwordGroup.touched && !passwordGroup.valid">
    <div *ng-if="passwordGroup.touched && !passwordGroup.valid">Passwords must match.</div>
    <div class="form-field">
        <label>Password</label>
        <input type="password" ng-control="password" placeholder="Your password" />
    </div>
    <div class="form-field">
        <label>Password Confirmation</label>
        <input type="password" ng-control="confirm" placeholder="Password Confirmation" />
    </div>
</div>
matthewdaniel
fuente
Cuando se ejecuta la validación de matching_password, ¿también se evalúa el control firstName? ¡Que no querría!
Pascal
16

Para ampliar la respuesta de matthewdaniel, ya que no es exactamente correcta. Aquí hay un código de ejemplo que muestra cómo asignar correctamente un validador a un ControlGroup.

import {Component} from angular2/core
import {FormBuilder, Control, ControlGroup, Validators} from 'angular2/common'

@Component({
  selector: 'my-app',
  template: `
    <form [ngFormModel]="form">
      <label for="name">Name:</label>
      <input id="name" type="text" ngControl="name">
      <br>
      <label for="email">Email:</label>
      <input id="email" type="email" ngControl="email">
      <br>
      <div ngControlGroup="matchingPassword">
        <label for="password">Password:</label>
        <input id="password" type="password" ngControl="password">
        <br>
        <label for="confirmPassword">Confirm Password:</label>
        <input id="confirmPassword" type="password" ngControl="confirmPassword">
      </div>
    </form>
    <p>Valid?: {{form.valid}}</p>
    <pre>{{form.value | json}}</pre>
  `
})
export class App {
  form: ControlGroup
  constructor(fb: FormBuilder) {
    this.form = fb.group({
      name: ['', Validators.required],
      email: ['', Validators.required]
      matchingPassword: fb.group({
        password: ['', Validators.required],
        confirmPassword: ['', Validators.required]
      }, {validator: this.areEqual})
    });
  }

  areEqual(group: ControlGroup) {
    let val;
    let valid = true;

    for (name in group.controls) {
      if (val === undefined) {
        val = group.controls[name].value
      } else {
        if (val !== group.controls[name].value) {
          valid = false;
          break;
        }
      }
    }

    if (valid) {
      return null;
    }

    return {
      areEqual: true
    };
  }
}

Aquí hay un ejemplo de trabajo: http://plnkr.co/edit/Zcbg2T3tOxYmhxs7vaAm?p=preview

Cody L.
fuente
¿Qué pasa si agregamos botones de radio y marcamos cómo obtener el valor de estos dos?
Pardeep Jain
2
ControlGroupse elimina a favor de FormGroupcualquiera que esté mirando esto. Ejemplo de Docs y Learn Angular2
sofly
2

Aquí hay otra opción que pude encontrar que no depende de un todo o sub, ControlGroupsino que está vinculada directamente a cada uno Control.

El problema que tuve fue que los controles que dependían entre sí no estaban juntos jerárquicamente, por lo que no pude crear un archivo ControlGroup. Además, mi CSS se configuró para que cada control aprovechara las clases angulares existentes para determinar si mostrar el estilo de error, que era más complicado cuando se trataba de una validación de grupo en lugar de una validación específica de control. No fue posible intentar determinar si un solo control era válido, ya que la validación estaba vinculada al grupo de controles y no a cada control individual.

En mi caso, quería el valor de un cuadro de selección para determinar si se requeriría otro campo o no.

Esto se construye usando el Form Builder en el componente. Para el modelo de selección, en lugar de vincularlo directamente al valor del objeto de solicitud, lo he vinculado para obtener / establecer funciones que me permitirán manejar eventos "al cambiar" para el control. Entonces podré configurar manualmente la validación para otro control dependiendo del nuevo valor de los controles seleccionados.

Aquí está la parte de la vista relevante:

<select [ngFormControl]="form.controls.employee" [(ngModel)]="employeeModel">
  <option value="" selected></option>
  <option value="Yes">Yes</option>
  <option value="No">No</option>
</select>
...
<input [ngFormControl]="form.controls.employeeID" type="text" maxlength="255" [(ngModel)]="request.empID" />

La porción del componente relevante:

export class RequestComponent {
  form: ControlGroup;
  request: RequestItem;

  constructor(private fb: FormBuilder) {
      this.form = fb.group({
        employee: new Control("", Validators.required),
        empID: new Control("", Validators.compose([Validators.pattern("[0-9]{7}"]))
      });

  get employeeModel() {
    return this.request.isEmployee;
  }

  set employeeModel(value) {
    this.request.isEmployee = value;
    if (value === "Yes") {
      this.form.controls["empID"].validator = Validators.compose([Validators.pattern("[0-9]{7}"), Validators.required]);
      this.form.controls["empID"].updateValueAndValidity();
    }
    else {
      this.form.controls["empID"].validator = Validators.compose([Validators.pattern("[0-9]{7}")]);
      this.form.controls["empID"].updateValueAndValidity();
    }
  }
}

En mi caso, siempre tuve una validación de patrón vinculada al control, por lo validatorque siempre se establece en algo, pero creo que puede establecerla validatoren nulo si no tiene ninguna validación vinculada al control.

ACTUALIZACIÓN: Existen otros métodos para capturar el cambio de modelo como (ngModelChange)=changeFunctionName($event)o suscribirse a cambios de valor de control mediante el uso dethis.form.controls["employee"].valueChanges.subscribe(data => ...))

Daniel Sara
fuente
1

También estaba buscando esto y terminé usando el equalTopaquete ng2-validation ( https://www.npmjs.com/package/ng2-validation )

Aquí hay un ejemplo: Basado en plantilla:

<input type="password" ngModel name="password" #password="ngModel" required/>
<p *ngIf="password.errors?.required">required error</p>
<input type="password" ngModel name="certainPassword" #certainPassword="ngModel" [equalTo]="password"/>
<p *ngIf="certainPassword.errors?.equalTo">equalTo error</p>

Impulsado por modelo:

let password = new FormControl('', Validators.required);
let certainPassword = new FormControl('', CustomValidators.equalTo(password));

this.form = new FormGroup({
  password: password,
  certainPassword: certainPassword
});

Modelo:

<form [formGroup]="form">
  <input type="password" formControlName="password"/>
  <p *ngIf="form.controls.password.errors?.required">required error</p>
  <input type="password" formControlName="certainPassword"/>
  <p *ngIf="form.controls.certainPassword.errors?.equalTo">equalTo error</p>
</form>
Baidaly
fuente
1

Aquí está mi versión que utilicé para asegurar que una edad en un campo sea mayor o igual a la edad en otro campo. También estoy usando grupos de formularios, así que uso la group.getfunción en lugar degroup.controls[]

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

export function greaterThanOrEqualTo(sourceKey: string, targetKey: string) {
    return (group: FormGroup) => {
        let sourceInput = group.get(sourceKey);
        let targetInput = group.get(targetKey);

        console.log(sourceInput);
        console.log(targetInput);

        if (targetInput.value < sourceInput.value) {
            return targetInput.setErrors({ notGreaterThanOrEqualTo: true })
        }
    }
}

Y en el componente:

    this.form = this._fb.group({

        clientDetails: this._fb.group({
            currentAge: ['', [Validators.required, Validators.pattern('^((1[89])|([2-9][0-9])|100)$')]],
            expectedRetirementAge: ['', [Validators.required]]
        }),

    },
    {
        validator: greaterThanOrEqualTo('clientDetails.currentAge', 'clientDetails.expectedRetirementAge')
    });
Adam Hockemeyer
fuente
0

Creo que tu mejor opción, por ahora, es crear un grupo de formularios para mantener tus controles. Cuando crea una instancia de su Control, pase la función para validarlo. ejemplo:

    this.password = new Control('', Validators.required);
    let x = this.password;
    this.confirm = new Control('', function(c: Control){
        if(typeof c.value === 'undefined' || c.value == "") return {required: "password required"};
        if(c.value !== x.value)
            return {error: "password mismatch"};
        return null;
    });

Sé que esto depende en gran medida de la versión de angularjs2 que esté ejecutando. Esto fue probado contra 2.0.0-alpha.46

Si alguien tiene una sugerencia mejor, como escribir un validador personalizado (que puede ser la mejor manera de hacerlo), es bienvenido.

EDITAR

también puede usar ControlGroup y validar ese grupo por completo.

this.formGroup = new ControlGroup({}, function(c: ControlGroup){
        var pass: Control = <Control>c.controls["password"];
        var conf: Control = <Control>c.controls["confirm"];
        pass.setErrors(null, true);
        if(pass.value != null && pass.value != ""){
            if(conf.value != pass.value){
                pass.setErrors({error: "invalid"}, true);
                return {error: "error"};
            }
        }
        return null;
    });

Simplemente edite los mensajes según su dominio.

Bruno Pires Lavigne Quintanilh
fuente
0

La respuesta de Louis Cruz fue muy útil para mí.

Para completar simplemente agregue en el else el restablecimiento de setErrors: return passwordConfirmationInput.setErrors (null);

¡Y todo funciona bien!

Gracias,

Saludos,

TGA

TGA
fuente
0

Angular 8 Ejemplo de validación en el campo de confirmación de contraseña

FYI: esto no actualizará la validación en el campo passwordConfirm si se cambia el campo principal de "contraseña" después de que esta validación haya pasado. Pero, puede invalidar el campo de confirmación de contraseña cuando un usuario escribe en el campo de contraseña

<input
  type="password"
  formControlName="password"
  (input)="registerForm.get('passwordConfirm').setErrors({'passwordMatches': true})"
/>

register.component.ts

import { PasswordConfirmValidator } from './password-confirm-validator';
export class RegisterComponent implements OnInit {
  registerForm: FormGroup = this.createRegisterForm({
    username: new FormControl('', [Validators.required, Validators.email]),
    password: new FormControl('', [
      Validators.required,
      Validators.pattern('^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9]).{8,}$'),
      Validators.minLength(8)
    ]),
    passwordConfirm: new FormControl('', [
      Validators.required,
      PasswordConfirmValidator //custom validator
    ])
  });
}

validator.ts de confirmación de contraseña

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

export function PasswordConfirmValidator(control: AbstractControl) {
  if(void 0 === control){ return null; }
  if(
    void 0 !== control.parent &&
    void 0 !== control.parent.controls &&
    void 0 !== control.parent.controls['password'] &&
    control.parent.controls['password'].value === control.value
  ){
    return null;
  }
  return {passwordMatches: true};
}

register.component.html

{{registerForm.get('passwordConfirm').hasError('passwordMatches')}}
Tim Joyce
fuente
-2

Sugeriría usar la biblioteca ng-form-rules. Es una biblioteca impresionante para crear todo tipo de formularios con una lógica de validación desacoplada del componente y que puede depender de los cambios de valor de otras áreas del formulario. Tienen gran documentación , ejemplos y un video que muestra gran parte de su funcionalidad . Hacer una validación como esta, lo que está tratando de hacer es trivial.

Puede consultar su archivo README para obtener información de alto nivel y un ejemplo básico.

Chris Knight
fuente
2
No me gusta la idea de que haya una biblioteca para todo ... las bibliotecas no son la solución a este problema. A menudo, creará nuevos problemas con solo usar otra biblioteca, también debe mantener las cosas actualizadas cuando se actualiza Angular. ¿Por qué no utilizar formas angulares como pretende el marco?
Nadine
-3

Reglas de validación de coincidencias de contraseña de Angular 4.

Si necesita campos de control de errores, puede hacerlo.

createForm() {
    this.ngForm = this.fb.group({
       'first_name': ["", Validators.required ],
       'last_name' : ["", Validators.compose([Validators.required, Validators.minLength(3)]) ],
       'status' : ['active', Validators.compose([Validators.required])],
       'phone':[null],
       'gender':['male'],
       'address':[''],
       'email':['', Validators.compose([
          Validators.required, 
          Validators.email])],
       'password':['', Validators.compose([Validators.required])],
       'confirm_password':['', Validators.compose([Validators.required])]
    }, {validator: this.matchingPassword('password', 'confirm_password')});
  }

Entonces necesitas declarar este método en el constructormétodo Like as.

constructor(
    private fb: FormBuilder

    ) {
    this.createForm();
  }

En lugar de establecer un error en ControlGroup, hágalo en el campo real de la siguiente manera:

    matchingPassword(passwordKey: string, confirmPasswordKey: string) {
  return (group: FormGroup): {[key: string]: any} => {
    let password = group.controls[passwordKey];
    let confirm_password = group.controls[confirmPasswordKey];

    if (password.value !== confirm_password.value) {
      return {        
        mismatchedPasswords: true
      };
    }
  }
}

Parte HTML para el grupo de contraseñas

<form [formGroup]="ngForm" (ngSubmit)="ngSubmit()">
    <div class="form-group">
            <label class="control-label" for="inputBasicPassword"> Password <span class="text-danger">*</span></label>
                <input type="password" class="form-control" formControlName="password" placeholder="Password" name="password" required>
                <div class="alert text-danger" *ngIf="!ngForm.controls['password'].valid && ngForm.controls['password'].touched">This Field is Required.</div>
            </div>
            {{ngForm.value.password | json}}
            <div class="form-group">
            <label class="control-label" for="inputBasicPassword">Confirm Password <span class="text-danger">*</span></label>
                <input type="password" class="form-control" name="confirm_password" formControlName="confirm_password" placeholder="Confirm Password" match-password="password">

    <div class='alert text-danger' *ngIf="ngForm.controls.confirm_password.touched && ngForm.hasError('mismatchedPasswords')">
              Passwords doesn't match.
      </div>
    </div>
<button type="submit" [disabled]="!ngForm.valid" class="btn btn-primary ladda-button" data-plugin="ladda" data-style="expand-left" disabled="disabled"><span class="ladda-label">
            <i class="fa fa-save"></i>  Create an account
        <span class="ladda-spinner"></span><div class="ladda-progress" style="width: 0px;"></div>
        </span><span class="ladda-spinner"></span></button>
</form>
Md.Jewel Mia
fuente