我能否在Angular / Angular Material中以编程方式移动mat-horizontal-stepper的步骤?

148

我有一个关于Angular Material(与Angular 4+一起使用)的问题。假设在我的组件模板中,我添加了一个<mat-horizontal-stepper>组件,在每个步骤<mat-step>中,我都有用于导航组件的步进按钮。像这样...

<mat-horizontal-stepper>
  <mat-step>
    Step 1
    <button mat-button matStepperPrevious type="button">Back</button>
    <button mat-button matStepperNext type="button">Next</button>
  </mat-step>
  <mat-step>
    Step 2
    <button mat-button matStepperPrevious type="button">Back</button>
    <button mat-button matStepperNext type="button">Next</button>
  </mat-step>
  <mat-step>
    Step 3
    <button mat-button matStepperPrevious type="button">Back</button>
    <button mat-button matStepperNext type="button">Next</button>
  </mat-step>
</mat-horizontal-stepper>
现在我在思考是否有可能将每个步骤中的按钮移除,并将它们放置在<mat-horizontal-stepper>的其他位置,甚至是在<mat-horizontal-stepper>之外,并且我可以使用我的组件typescript文件中的代码向前和向后导航。为了给你一个想法,我希望我的HTML看起来像这样。
<mat-horizontal-stepper>
    <mat-step>
        Step 1
    </mat-step>
    <mat-step>
        Step 2
    </mat-step>
    <mat-step>
        Step 3
    </mat-step>
    <!-- one option -->
    <div>
       <button mat-button matStepperPrevious type="button">Back</button>
       <button mat-button matStepperNext type="button">Next</button>
    </div>
</mat-horizontal-stepper>

<!-- second option -->
<div>
   <button (click)="goBack()" type="button">Back</button>
   <button (click)="goForward()" type="button">Next</button>
</div>
6个回答

275
是的。可以使用MatStepperselectedIndex属性跳转到特定的步骤。此外,MatStepper公开了next()previous()两个公共方法。您可以使用它们来前后移动。
在您的模板中: 添加一个id到您的步进器,例如#stepper。然后在您的goBack()goForward()方法中传递步进器id。
<mat-horizontal-stepper #stepper>
    <!-- Steps -->
</mat-horizontal-stepper>    
<!-- second option -->
<div>
   <button (click)="goBack(stepper)" type="button">Back</button>
   <button (click)="goForward(stepper)" type="button">Next</button>
</div>

..并且在您的TypeScript代码中:

import { MatStepper } from '@angular/material/stepper';

goBack(stepper: MatStepper){
    stepper.previous();
}

goForward(stepper: MatStepper){
    stepper.next();
}

链接到StackBlitz演示


您还可以使用ViewChild获取对步进器组件的引用,如下所示:

@ViewChild('stepper') private myStepper: MatStepper;

goBack(){
    this.myStepper.previous();
}

goForward(){
    this.myStepper.next();
}
在这种情况下,您不需要在组件的HTML中的方法中传递步进器引用。链接到使用ViewChild演示
您可以使用以下内容启用/禁用“后退”和“下一步”按钮:
<button (click)="goBack(stepper)" type="button" 
        [disabled]="stepper.selectedIndex === 0">Back</button>
<button (click)="goForward(stepper)" type="button" 
        [disabled]="stepper.selectedIndex === stepper._steps.length-1">Next</button>

11
我刚刚在看 ViewChild,想知道如何引用 Stepper - 但是你已经超过我了!很喜欢你添加的禁用/启用功能!给你点赞! - Mike Sav
ViewChild 也是获取 stepper 的好选择,但我更喜欢传递一个 id。我在演示中也添加了 ViewChild 解决方案 \o/ - FAISAL
嗨Faisal,感谢您提供如此出色的答案,还有一个问题,请问我们是否可以将Angular组件传递给mat-step,然后根据该组件的有效性来遍历到下一个mat-step?如何实现这一点?谢谢。 - Enthu
这里已完成的步骤图标设置为“编辑”,当前步骤图标设置为“数字”,我可以将已完成的步骤图标设置为“完成(勾号)”,当按下返回按钮时,已完成的步骤图标更改为“编辑”...例如:假设我完成了第2步并单击了下一步按钮,现在第2步的图标显示为“编辑图标(笔符号)”,现在我在第3步中,它显示为“数字”作为图标...我想要做的是,当从第2步单击下一步按钮时,图标应为“完成”,第3步图标应为“数字”,如果我单击第3步的返回按钮,则第3步的图标应为“完成”,第2步的图标应为“编辑”。 - Zhu
1
如果您正在使用 Angular 8,并且使用 @ViewChild,请使用 _static: false_。@ViewChild('stepper', {static: false}) private myStepper: MatStepper;根据Angular文档,在Angular 9+中,这个值默认为 _false_。 链接 - rorvis
显示剩余5条评论

47

除了 @Faisal 的回答外,以下是我对于在不需要将步进器传递为参数的情况下使 MatStepper 跳转的看法。

当您需要更多灵活性以操纵步进器时(例如从 Service 或其他地方),这将很有帮助。

HTML:

<div fxLayout="row" fxLayoutAlign="center center" fxLayoutGap="6px">
  <button (click)="move(0)">1st</button>
  <button (click)="move(1)">2nd</button>
  <button (click)="move(2)">3rd</button>
  <button (click)="move(3)">4th</button>
</div>

TS 文件:

move(index: number) {
    this.stepper.selectedIndex = index;
}

这是一个StackBlitz演示


使用mat-horizontal-stepper时,“linear = true”是否可行? - Jignesh Panchal

33

如果你想通过编程方式导航到下一步,并且你正在使用线性步进器,请按以下步骤操作:

  • 创建像这样的stepper<mat-horizontal-stepper linear #matHorizontalStepper>

  • 定义mat-step,如下所示:<mat-step [completed]="isThisStepDone">

  • mat-step内部创建一个用于转到下一步的按钮,如下所示:<button (click)="next(matHorizontalStepper)">下一步</button>

  • .ts文件中声明一个名为stepperMatStepper引用:@ViewChild('matHorizontalStepper') stepper: MatStepper;

  • 此外,在.ts文件中将isThisStepDone初始化为falseisThisStepDone: boolean = false;

  • 然后编写一个名为NEXT STEP的方法,用于处理next()按钮:

 submit(stepper: MatStepper) {
  this.isThisStepDone = true;
  setTimeout(() => {           // or do some API calls/ Async events
   stepper.next();
  }, 1);
 }

注意:由于状态传递通过isThisStepDone进行,因此异步部分(setTimeout())是必需的。


7
由于状态通过 isThisStepDone 进行传播,因此需要异步部分(setTimeout())。 - Yuri
1
谢谢,兄弟。这就是我要找的线性步进电机。 - Parthan_akon
您的示例代码中的方法名称有误。应该是“next”,而不是“submit”。 - Todd
有没有办法使用当前步骤的完成状态来禁用/启用按钮? - Wouter Lievens

6
如果您在子组件中,您可以注入步进器。
MyMainPageWithStepper.html (simplified)

<mat-horizontal-stepper>
  <mat-step label="Upload">
    <my-component></my-component>
  </mat-step>
</mat-horizontal-stepper>

MyComponent.ts

constructor(private readonly _stepper: CdkStepper}{}

someFunction(): void {
   this._stepper.next();
}

3
您也可以通过使用selectedIndex指定步进器的实际索引来完成此操作。
stackblitz: https://stackblitz.com/edit/angular-4rvy2s?file=app%2Fstepper-overview-example.ts HTML:
<div class="fab-nav-container">
   <mat-vertical-stepper linear="false" #stepper>
       <mat-step *ngFor="let step of stepNodes; let i = index">
           <ng-template matStepLabel>
               <p> {{step.title}} </p>
           </ng-template>
       </mat-step>
   </mat-vertical-stepper>
</div>

<div class="button-container">
   <div class="button-grp">
      <button mat-stroked-button (click)="clickButton(1, stepper)">1</button>
      <button mat-stroked-button (click)="clickButton(2, stepper)">2</button>
      <button mat-stroked-button (click)="clickButton(3, stepper)">3</button>
   </div>
</div>

TS:

import {Component, OnInit, ViewChild} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import { MatVerticalStepper } from '@angular/material';
import { MatStepper } from '@angular/material';
export interface INodes {
    title: string;
    seq: number;
    flowId: string;
}
/**
 * @title Stepper overview
 */
@Component({
  selector: 'stepper-overview-example',
  templateUrl: 'stepper-overview-example.html',
  styleUrls: ['stepper-overview-example.scss'],
})
export class StepperOverviewExample implements OnInit {
  @ViewChild(MatVerticalStepper) vert_stepper: MatVerticalStepper;
  @ViewChild('stepper') private myStepper: MatStepper;

  stepNodes: INodes[] = [
    { title: 'Request Submission', seq: 1, flowId: 'xasd12'}, 
    { title: 'Department Approval', seq: 2, flowId: 'erda23'}, 
    { title: 'Requestor Confirmation', seq: 3, flowId: 'fsyq51'}, 
  ];

  ngOnInit() {
  }
  ngAfterViewInit() {
    this.vert_stepper._getIndicatorType = () => 'number';
  }
  clickButton(index: number, stepper: MatStepper) {
      stepper.selectedIndex = index - 1;
  }
}

1
我只是在我的函数中使用了 @ViewChild('stepper') private myStepper: MatStepper;this.matStepper.next();。工作得非常完美。 - Max

1
我实现的解决方案允许先前步骤的验证(它填充验证点):
  ngAfterViewInit() {
        if(this.isDone) {
          this.stepper.selectedIndex = 0;
          this.stepper.linear = false;
    
          // remove the validation // which makes the next call to the next method validate the step. warning => the validation should be readded.
          this.firstFormGroup = this._formBuilder.group({
            email: ['',],
            password:['',]
          });
    
          this.stepper.next();
          
          this.secondFormGroup = this._formBuilder.group({
            pricingYear: ['',],
            pricingMontly: ['',],
          });
          this.stepper.next();
    
          setTimeout(() => {
            this.stepper.linear = true;
         });
        }
      }

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