Angular 反应式表单验证和数据形成问题

Angular Reactive form validation and data formation issue

尝试至少对 select 个 一个 项目添加验证 mat-select multipe option。目前在页面加载时显示错误消息,直到用户 select 从 select 选项中选择一项,该选项工作正常。

期望:
当 i select all 或 single select 时,错误消息应该消失。 (预期且工作正常)。

但是发生了什么:
当我删除select selected 单个项目时,未显示必需的错误消息。

不知道我做错了什么。

skillForm.component.ts

skillList = [
    { skillId: '0', skillName: 'JS' },
    { skillId: '1', skillName: 'TS' },
    { skillId: '2', skillName: 'JAVA' },
    { skillId: '3', skillName: '.Net' },
];

@ViewChild('pickAllCourse') private pickAllCourse: MatOption;
trainerForm = FormGroup;

constructor(public formBuilder: FormBuilder) { }

this.trainerForm = this.formBuilder.group({
    selectedSkills :['', Validators.required, Validators.minLength(1)]
})

pickAll(): void {
    if(this.pickAllCourse.selected) {
    this.trainerForm.controls.selectedSkills.patchValue([...this.skillList.map((item) => item.deviceId), 0]);
    } else {
        this.trainerForm.controls.selectedSkills.patchValue([]);
    }
}


selectOneItem(all): any {
    if (this.pickAllCourse.selected) {
        this.pickAllCourse.deselect();
        return false;
    }
    if (this.trainerForm.controls.selectedSkills.value.length === this.skillList.length) {
        this.pickAllCourse.select();
    }
}

onSubmit(): void{
    console.log('form value', this.trainerForm.value)
    
    // 
}

skillForm.component.html

    <mat-form-field class="selectedSkills">
        <mat-select multiple ngDefaultControl formControlName="selectedSkills"
            placeholder="Select Device Type">
            <mat-option #pickAllCourse (click)="pickAll()" [value]="0">All</mat-option>
            
        <mat-option *ngFor="let i of skillList" [value]="i.deviceId"
            (click)="selectOneItem(pickAllCourse.viewValue)">{{ i.skillName }}
        </mat-option>
        
        </mat-select>
<span class="text-danger" *ngIf="trainerForm.controls['selectedSkills '].invalid ">This field is required</span>
    </mat-form-field>

此外,我需要帮助来了解如何在为后端提交表单时构建如下所示的对象。

skillList: [
    {skillId: '0'},
    {skillId: '1'}
];

当我执行 console.log this.trainerForm.value 时,我看到 skillList: ['0']

问题和疑虑

问题 1:

trainerForm.controls['selectedSkills '].

上的额外间距存在拼写错误
<span class="text-danger" *ngIf="trainerForm.controls['selectedSkills '].invalid ">This field is required</span>

问题 2:

如果表单控件需要多个 Validators,您应该 用数组 将它们分组。

this.trainerForm = this.formBuilder.group({
  selectedSkills :['', Validators.required, Validators.minLength(1)]
})

更改为:

this.trainerForm = this.formBuilder.group({
  selectedSkills :['', [Validators.required, Validators.minLength(1)]]
})

关注1

从 HTML 和 Typescript 部分,selectedSkills 将 return 一个 number 的数组,但不是 Object 的数组。当您使用 item.deviceId (return string) 并且 deviceId 不存在于 skillListsObject 中。我假设您使用的是 item.skillId.

pickAll(): void {
  if(this.pickAllCourse.selected) {
    this.trainerForm.controls.selectedSkills.patchValue([...this.skillList.map((item) => item.deviceId), 0]);
  } else {
    this.trainerForm.controls.selectedSkills.patchValue([]);
  }
}
<mat-option *ngFor="let i of skillList" [value]="i.deviceId"
    (click)="selectOneItem(pickAllCourse.viewValue)">{{ i.skillName }}
</mat-option>

因此,当你console.log(this.trainerForm.value)时,它会显示:

{ selectedSkills: [1, 2, 3] }

解决方案

  1. 对于使用 *ngFor 生成的 <mat-option>,将 [value]="{ skillId: i.skillId }" 设置为 return 选择值作为 object
  2. 为您的 <mat-select> 添加 compareWith。目的是比较 this.trainerForm.controls.selectedSkills[value]="{ skillId: i.skillId }" 到 check/uncheck 选项时 select/deselect All.
<mat-form-field class="selectedSkills">
    <mat-select
      multiple
      ngDefaultControl
      formControlName="selectedSkills"
      placeholder="Select Device Type"
      [compareWith]="compareFn"
    >
      <mat-option #pickAllCourse (click)="pickAll()" [value]="0"
        >All</mat-option
      >

      <mat-option
        *ngFor="let i of skillList"
        [value]="{ skillId: i.skillId }"
        (click)="selectOneItem(pickAllCourse.viewValue)"
        >{{ i.skillName }}
      </mat-option>
    </mat-select>
    <span
      class="text-danger"
      *ngIf="trainerForm.controls['selectedSkills'].invalid"
      >This field is required</span
    >
</mat-form-field>
  1. 问题 2.
  2. 中所述,在数组 [] 中设置多个 Validators
  3. pickAll()patchValue 对于 this.trainerForm.controls.selectedSkills 作为 { skillId: item.skillId } Object.
  4. onSubmit() 在将表单值传递给 API 之前,请确保仅使用 { skillId: item.skillId } Object 过滤 skillSets 值。
  5. compareFn 用于将选定的 skillSets 值与每个 <mat-option> 值进行比较。因此,当 Select All 时,所有 <mat-option> 将被选中,反之亦然,如 (2).
trainerForm: FormGroup;

ngOnInit() {
  this.trainerForm = this.formBuilder.group({
    selectedSkills: ['', [Validators.required, Validators.minLength(1)]],
  });
}

pickAll(): void {
  if (this.pickAllCourse.selected) {
    this.trainerForm.controls.selectedSkills.patchValue([
      ...this.skillList.map((item) => {
        return {
          skillId: item.skillId
        };
      }),
      0,
    ]);
  } else {
    this.trainerForm.controls.selectedSkills.patchValue([]);
  }
}

onSubmit(): void {
  console.log('form value', this.trainerForm.value);

  let postFormValue: any = {
    ...this.trainerForm.value,
    selectedSkills: this.trainerForm.value.selectedSkills.filter(
      (x: any) => typeof x === 'object'
    ),
  };

  console.log(postFormValue);
}

compareFn(obj1: any, obj2: any): boolean {
  return obj1 && obj2 ? obj1.skillId === obj2.skillId : obj1 === obj2;
}

Sample Solution on StackBlitz