Best way to implement Angular cross field validation

淺唱寂寞╮ 提交于 2019-12-08 14:12:19

问题


I am trying to figure out the best way to implement cross field validation in Angular.

For example, I have a select field that makes another field mandatory.

I want to be able to:

  • Change the border color of the field if it is invalid
  • Display a * in front of the field whenever it becomes mandatory
  • Display a specific error message that explains what validation rule is broken.

So far, I came up with three solutions but they don't feel so convincing to me.

  • Listen to select field changes and update second field's validators.
  • Listen to both fields changes and manually perform setErrors
  • Lift validation to formGroup (which can feel extremely cumbersome since, validation state is now stored in formGroup and not directly available in formControl).

Here is a Stackblitz implementation that demos my investigations.


回答1:


For cross field validation, you can use required validation of @rxweb/reactive-form-validation.

You just have to mention conditionalExpression in your formControl like this:

input2:['', RxwebValidators.required({conditionalExpression:'x => x.input1 == "mandatory"' })]

and set the error message in your app.component.ts like this

ngOnInit(){
    ReactiveFormConfig.set({"validationMessage":{"required":"This field is required"}});
  }

Here is your complete component code:

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from "@angular/forms"
import { RxwebValidators } from '@rxweb/reactive-form-validators';

@Component({
    selector: 'app-required-conditionalExpression-validator',
    templateUrl: './required-conditional-expression.component.html'
})
export class RequiredConditionalExpressionValidatorComponent implements OnInit {
    userFormGroup: FormGroup

    constructor(
        private formBuilder: FormBuilder )
    { }

    ngOnInit() {
        this.userFormGroup = this.formBuilder.group({
            input1:[''], 
            input2:['', RxwebValidators.required({conditionalExpression:'x => x.input1 == "mandatory"' })], 
        });
    }
}

Here is your Complete HTML Code:

<div>
    <form [formGroup]="userFormGroup">

        <div>
            <label>Mandatory/Optional </label>
        <select formControlName="input1">
          <option value="optional">Optional</option>
          <option value="mandatory">Mandatory</option>
        </select>
      <br/><br/>
    </div>
    <div>
      <label>Input2</label>
      <input type="text" formControlName="input2"/><br/>
      <span>
        {{userFormGroup.controls.input2.errors?.required?.message}}
      </span>
    </div>
  </form>
</div>

Here is the Working Example




回答2:


the best way is the simpler way. Just use a customValidator like

  form:FormGroup=new FormGroup({
    input1: new FormControl('optional'),
    input2: new FormControl(null,this.customValidator())
  })

  customValidator()
  {
    return ((control:any)=>{
      if (!control.parent)
        return null;

      let mandatory=control.parent.get('input1').value;
      return mandatory!='optional' && !control.value?{required:true}:null
    })
  }

Another option for not ask for control.parent it's use bind(this). This allow us have inside the validator to all the variables of our component, and of course access to this.form

  form:FormGroup=new FormGroup({
    input1: new FormControl('optional'),
    input2: new FormControl(null,this.customValidator().bind(this)) //<--bind(this)
  })

  customValidatorBind()
  {
    return ((control:any)=>{
      if (!this.form)
        return null;

      let mandatory=this.form.get('input1').value;
      return mandatory!='optional' && !control.value?{required:true}:null
    })
  }

Well, as we want that when change 'input1' 'input2' was checked, you need use, after create the form subscribe to valueChanges

this.form.get('input1').valueChanges.subscribe(()=>{
  this.form.get('input2').updateValueAndValidity()
})

UPDATED there's another aproach. Create the customValidator over the form and use the validator to use setError to the control required. Using setError, make that Angular add ng-invalid for us, ad we NEEDN't subscribe to value change. See

    form:FormGroup=new FormGroup({
        input1: new FormControl('optional'),
        input2: new FormControl(null)
      },{validators:this.customValidatorForm()})

  customValidatorForm()
  {
    return (form:FormGroup)=>{
      const error=form.get('input1').value!='optional' &&
           !form.get('input2').value?{required:true}:null;
      form.get('input2').setErrors(error); //<--see the setErrors
      return error;
    }
  }

See stackblitz



来源:https://stackoverflow.com/questions/57120135/best-way-to-implement-angular-cross-field-validation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!