Angular Material Stepper 导致 mat-form 字段在返回到旧步骤时验证动态表单

Angular Material Stepper causes mat-formfield to validate for dynamic forms when returning to an older step

使用动态表单时 angular materials mat-stepper 出现问题。例如,我有一个包含 3 个步骤的步进器,每个步骤都有自己的形式。然而,第一步使用隐藏表单来确定它是否有效,因为这一步可以动态添加更多表单,因此不可能将所有表单绑定到该步骤。

当您处于第一步时,您可以创建多个表单,并且一切都按预期工作,而不会对添加的新表单进行任何随机验证。如果您转到第 2 步或第 3 步并返回到第一步,然后创建一个新表单,它将自动开始并以红色突出显示所有字段。

我尝试了很多不同的尝试来压制但是我没有成功。下面是一个基本示例,说明我的第一步如何包含绑定到步骤控件的 hiddenForm、默认表单和用于在该步骤中创建更多表单的按钮。

我对尝试解决此问题的研究使我相信 material 步进器会使所有 mat-form-fields 在无效时变为红色,无论是否新添加。

<mat-horizontal-stepper [linear]="true">
    <mat-step [stepControl]="hiddenForm" label="step 1"
      <form [formGroup]="myForm">
        <mat-form-field>
          <mat-label>
            First Name
          </mat-label>

          <input [formControl]="firstName"matInput>

          <mat-error *ngIf="!firstName.valid && firstName.touched">
            First Name required
          </mat-error>
        </mat-form-field>
      </form>

    <button (click)="AddNewForm()">Add New Form</button>
  </mat-step>
</mat-horizontal-stepper>

尝试失败次数:(当前表格为新增表格)

this.currentForm.markAsUntouched();
this.currentForm.markAsPristine();
this.currentForm.setErrors(null);
this.currentForm.reset();

this.currentForm.get('firstName).setErrors(null);
this.currentForm.get('firstName).reset();
this.currentForm.get('firstName).markAsPristine();
this.currentForm.get('firstName).markAsUntouched();

<mat-step [completed]="true" ..> ( on all steps )

背景资料

我找到的最好的解决方案是更改 mat-stepper 上的参数。在任何给定时间都选择了一个步骤。在步骤上,您可以更改步骤是否已与之交互。如果之前访问过某个步骤,则 interacted 参数将设置为 true。由于这是有意且有意义的,因此将 class 添加到所有 mat-form-fields 导致它们变红会导致问题。

这会导致糟糕的用户体验的场景:

  1. 您完成第一步,进入第二步。进入第二步后,您意识到自己在第一步中犯了错误,并决定返回到第一步。您进行更改,然后再次进行第二步。因为如果您有 mat-form-fields,您已经访问过此步骤,将添加 class(可能还有其他更改)并且您的表单字段现在全部为红色。这是一个糟糕的用户体验,因为用户在技术上没有犯任何错误并且可以承受。

  2. 第一步是创建动态表单。为简单起见,让我们使用英雄之旅类比。在我们的第一步中,您可以动态添加表单。每种形式代表您要添加的新英雄。您已经添加了 3 个英雄并继续进行第 2 步。在完成第 2 步之前,您意识到您忘记了一些英雄并继续回到第 1 步。现在当您单击我们的按钮 'create hero' 时,您的动态表单会弹出,但您所有的 mat-form-fields 现在是红色的,就像用户犯了错误一样。这是另一个糟糕的用户体验。

修复:

hero.stepper.html

<mat-horizontal-stepper [linear]="true" 
  (selectionChange)="stepChanged($event, stepper);">

  <mat-step [stepControl]="hiddenForm" label="step 1"
    <form [formGroup]="heroFormGroupArray[0]">
      <mat-form-field>
        <mat-label>Hero Name</mat-label>

        <input [formControl]="heroName"matInput>

        ...
      </mat-form-field>
    </form>

    <button (click)="AddNewHero()">Add New Hero</button>
  </mat-step>
</mat-horizontal-stepper>

hero.stepper.ts

export class heroStepComponent implements OnInit {
  constructor(){
    ...
  }

  ngOnInit(){
    ...
  }

  stepChanged(event, stepper){
    stepper.selected.interacted = false;
  }
}

根据之前的 ,我发现了 stepChanged 函数的更好实现:

  stepChanged(event: StepperSelectionEvent) {
    if (event.previouslySelectedIndex > event.selectedIndex) {
     event.previouslySelectedStep.interacted = false;
    }
  }

此代码将仅在您转到之前步骤的步骤上设置交互 属性。