The demo address of this article: source code or final effect

In the project, we sometimes need to move the validation of the form to do dynamic planning. For example, two kinds of users are registered simultaneously in one registration interface, but the input items of the two kinds of users are not the same.

If you are a teacher, you are required to enter a job number:
image.png

If you are a student user, you are required to enter the student ID:
image.png

We call this scenario dynamic form validation.

In the validation of the above table, we require:

  1. Work number and student number do not interfere with each other.
  2. When selecting a teacher type, it is only judged whether the job number has been entered.
  3. When selecting a student type, it is only judged whether the student number has been entered.

Implementation plan

In fact, there are many ways to implement this. There are roughly three types of the ones we've used in the project:

  1. Use cross-field validators.
  2. Subscribe to the user type and reset the verification rules for the worker ID or student ID when the user type is changed.
  3. Subscribe to the user type, when the user type is changed, add or remove the fromGroup ID, student ID FromControl in 0623483c8a199d.

Cross field validator

Anguar's official gives an example of using a cross-field validator in , the idea is to add a validator to FromGroup , and then get the value of FormControl in the validator, and verify it according to the specific situation.

advantage:

  1. Official example, low learning cost.
  2. The verification is directly put into the validator, and the logic is clear.
  3. The validator has no effect on getting the value of FromGroup .

shortcoming:

  1. It is not possible to define validation conditions directly in FormControl , which is not intuitive.
  2. Only error messages can be displayed uniformly, and error messages cannot be customized for a single field.

You can click https://segmentfault.com/a/1190000041563611 to see the implementation example.

reset validation rules

FromControl provides clearValidators() to clear the validator, and setValidators() to set the validator, so we can subscribe whether the user type changes, when the change occurs, clear the validator of the cross field according to the situation, and then re-set its validator.

advantage:

  1. Provides a new idea for dynamically adding asynchronous validators

shortcoming:

  1. Validation rules are not intuitive.
  2. A lot of code.

Reset FromGroup items

FromGroup provided by removeControl() allows us to remove the FormControl in it. Using this mechanism, we can remove and add the corresponding FormControl according to the situation after the subscription user type changes, so as to achieve the purpose of dynamically validating the form.

Example code C layer:

export class AppComponent implements OnInit {
  name = 'Angular ' + VERSION.major;
  formGroup = new FormGroup({});
  // 学号
  studentNoFormControl = new FormControl(null, Validators.required);
  // 工号
  teachterNoFormControl = new FormControl(null, Validators.required);
  // 用户类型
  typeFormControl = new FormControl(null, Validators.required);
  ngOnInit(): void {
    this.formGroup.addControl('name', new FormControl('', Validators.required));
    this.formGroup.addControl('type', this.typeFormControl);

    // 订阅类型的变化,从而决定在formGroup中添加学号还是工号FormControl
    this.typeFormControl.valueChanges.subscribe((type) => {
      if (type === 0) {
        this.formGroup.removeControl('studentNo');
        this.formGroup.addControl('teacherNo', this.teachterNoFormControl);
      } else {
        this.formGroup.removeControl('teacherNo');
        this.formGroup.addControl('studentNo', this.studentNoFormControl);
      }
    });

    // 初始化用户类型为教师
    this.typeFormControl.setValue(0);
  }

  onSubmit(): void {
    alert('submit');
  }

  /**
   * 显示学号或是工号的input
   */
  showStudent(): boolean {
    return this.typeFormControl.value === 1;
  }
}

Layer V:

<hello name="{{ name }}"></hello>
<p>表单动态验证示例</p>
<pre>{{ formGroup.invalid | json }}</pre>
<pre>{{ formGroup.get('type').value | json }}</pre>
<form [formGroup]="formGroup">
  <div>姓名:<input type="text" formControlName="name" /></div>
  <div>
    用户类型:
    <label
      ><input type="radio" [value]="0" formControlName="type" name="type" />
      教师</label
    >
    <label
      ><input type="radio" [value]="1" formControlName="type" name="type" />
      学生</label
    >
  </div>
  <div *ngIf="showStudent()">
    学号:<input type="text" formControlName="studentNo" />
  </div>
  <div *ngIf="!showStudent()">
    工号:<input type="text" formControlName="teacherNo" />
  </div>
  <button [disabled]="formGroup.invalid" (click)="onSubmit()">Submit</button>
</form>

advantage:

  1. Set the validator directly on FormControl , the code is intuitive.
  2. You can directly use the class attributes such as ngvalid provided by angular to quickly define the style of the validation result.

shortcoming:

  1. The validator has an effect on getting the value of FromGroup . For example, in the subsequent operation to obtain the relevant value of FormGroup , it is necessary to judge whether FormGroup has a value, which may easily cause the error of calling undefined on value . (This can be circumvented using ? ---- formGroup.get('xxx')?.value .

潘杰
3.1k 声望241 粉丝