Angular Material Stepper导致动态表单中的mat-formfield在返回旧步骤时进行验证。

13
在使用动态表单时,angular材料的mat-stepper出现问题。例如,我有一个带有3个步骤的stepper,每个步骤都有自己的表单。然而,第一步使用隐藏表单来确定它是否有效,因为此步骤可以动态地添加更多表单,因此将所有表单绑定到该步骤是不可能的。
当您在第一步时,您可以创建多个表单,并且一切都按预期工作,无需对新添加的表单进行任何随机验证。如果您转到第2或第3步,然后回到第1步并创建一个新表单,则所有字段都会自动以红色突出显示。
我尝试了许多不同的尝试来压制,但我没有成功。下面是我的第一步包含一个绑定到步骤控件的hiddenForm、一个默认表单和一个按钮来在该步骤中创建更多表单的基本示例。
我的研究试图修复这个问题使我相信,如果无效,材料stepper会使所有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 )
2个回答

24

背景信息

我找到的最好的解决方案是更改 mat-stepper 上的一个参数。在任何给定时间都有一个选定的步骤。在步骤中,您可以更改步骤是否已被交互。如果先前访问过某个步骤,则将设置 interacted 参数为 true。尽管这是有意的并且很有道理,但它会导致所有 mat-form-fields 添加一个类,并使它们变成红色,从而引起问题。

导致糟糕用户体验的情况:

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

  2. 在第一步中,您正在创建动态表单。为简单起见,让我们使用英雄之旅的比喻。在我们的第一步中,您可以动态添加表单。每个表单代表您要添加的新英雄。您已经添加了3个英雄并转到第二步。在完成第二步之前,您意识到忘记了几个英雄,并返回第一步。现在,当您点击“创建英雄”按钮时,您的动态表单弹出,但是您所有的 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>

英雄.步进器.ts

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

  ngOnInit(){
    ...
  }

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

非常感谢您。你让我的一天过得很愉快。我已经挣扎了两天来解决这个问题。非常感谢。 - Venkateswaran R
没问题,你也可以狠狠地攻击我哈哈。 - L1ghtk3ira
2
尽管这似乎有效,但它会破坏步进器的“next()”函数的功能。https://imge.to/i/LRq7F - George Knap
@GeorgeKnap,我无法加载gif。如果您有时间,建议您深入挖掘一下为什么在您尝试实现时它没有起作用。以上内容已在生产中使用,用户或QA未报告任何问题。如果有特定的问题,我自己也非常感兴趣了解原因。我会尝试解决,但我无法复制此问题。谢谢。 - L1ghtk3ira
@L1ghtk3ira 如果我没有使用 (selectionChange)="stepChanged($event, stepper); 在 <mat-horizontal-header> 中,如何将交互标记为 false?从哪里获取 stepper 值? 我正在使用函数移动 stepper: this.myStepper.previous() this.myStepper.next() - Sanam
显示剩余4条评论

11

接着之前的帖子,我找到了更好的stepChanged函数实现:

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

此代码将仅在您从前一步骤跳转到的步骤上设置interacted属性。


太棒了,无话可说!!几个小时后,找到了这个,真是太神奇了!非常感谢! - Nicolae Olariu
对我来说,在当前步骤上设置 interacted(而不是上一个步骤)就可以解决问题了。 - Dan Macak
我一直在解决 mat-form-field-invalid 的问题。我将其更改为每次导航到特定步骤索引时的 event.selectedStep.interacted = false; 现在 Angular FormGroup 和 MatFormFieldInvalid 工作正常。 - SoEzPz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接