You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Material Dialog与响应式表单构建动态可复用自动化助手步骤器

Angular 动态步骤复用组件实现方案

核心思路

通过TemplateRef结合ngTemplateOutlet实现步骤内容的动态注入,搭配可配置的步骤数组,适配不同用户类型的多流程(3步/5步注册等)复用需求。

实现步骤

1. 定义步骤配置接口

创建统一的步骤配置结构,包含步骤标题和内容模板引用:

import { TemplateRef } from '@angular/core';

export interface StepConfig {
  title: string;
  contentTemplate: TemplateRef<any>;
  // 可扩展:添加disabled、completed等状态字段
}

2. 封装通用步骤组件

构建可复用的步进器组件,接收步骤配置数组并动态渲染内容:

import { Component, Input, Output, EventEmitter } from '@angular/core';
import { StepConfig } from './step-config.interface';

@Component({
  selector: 'app-reusable-stepper',
  template: `
    <mat-stepper linear>
      <mat-step 
        *ngFor="let step of steps; let i = index"
        [label]="step.title"
        [completed]="step.completed"
        [disabled]="step.disabled"
      >
        <ng-container [ngTemplateOutlet]="step.contentTemplate"></ng-container>
        <div>
          <button mat-button (click)="onNext(i)" *ngIf="i < steps.length - 1">下一步</button>
          <button mat-button (click)="onPrev(i)" *ngIf="i > 0">上一步</button>
          <button mat-button (click)="onFinish()" *ngIf="i === steps.length - 1">完成</button>
        </div>
      </mat-step>
    </mat-stepper>
  `
})
export class ReusableStepperComponent {
  @Input() steps: StepConfig[] = [];
  @Output() next = new EventEmitter<number>();
  @Output() prev = new EventEmitter<number>();
  @Output() finish = new EventEmitter<void>();

  onNext(index: number) {
    this.next.emit(index);
  }

  onPrev(index: number) {
    this.prev.emit(index);
  }

  onFinish() {
    this.finish.emit();
  }
}

注:示例使用Angular Material步进器,可替换为自定义步进器结构。

3. 父组件中配置并传入模板

在业务组件中定义各步骤的内容模板,根据业务逻辑动态组装步骤数组:

import { Component, ViewChild, TemplateRef, OnInit } from '@angular/core';
import { StepConfig } from './step-config.interface';

@Component({
  selector: 'app-root',
  template: `
    <app-reusable-stepper 
      [steps]="currentSteps"
      (next)="handleNext($event)"
      (prev)="handlePrev($event)"
      (finish)="handleFinish()"
    ></app-reusable-stepper>

    <!-- 步骤1模板:基础信息 -->
    <ng-template #basicInfoTpl>
      <div class="step-content">
        <h4>基础信息填写</h4>
        <mat-form-field>
          <input matInput placeholder="用户名" required>
        </mat-form-field>
        <mat-form-field>
          <input matInput placeholder="邮箱" required>
        </mat-form-field>
      </div>
    </ng-template>

    <!-- 步骤2模板:身份验证 -->
    <ng-template #authTpl>
      <div class="step-content">
        <h4>身份验证</h4>
        <mat-form-field>
          <input matInput placeholder="验证码" required>
        </mat-form-field>
        <button mat-button>发送验证码</button>
      </div>
    </ng-template>

    <!-- 步骤3模板:密码设置 -->
    <ng-template #pwdSetTpl>
      <div class="step-content">
        <h4>设置登录密码</h4>
        <mat-form-field>
          <input matInput type="password" placeholder="密码" required>
        </mat-form-field>
      </div>
    </ng-template>

    <!-- VIP专属步骤模板 -->
    <ng-template #vipConfigTpl>
      <div class="step-content">
        <h4>VIP专属配置</h4>
        <mat-checkbox>开启专属客服通道</mat-checkbox>
      </div>
    </ng-template>
  `
})
export class AppComponent implements OnInit {
  @ViewChild('basicInfoTpl') basicInfoTpl!: TemplateRef<any>;
  @ViewChild('authTpl') authTpl!: TemplateRef<any>;
  @ViewChild('pwdSetTpl') pwdSetTpl!: TemplateRef<any>;
  @ViewChild('vipConfigTpl') vipConfigTpl!: TemplateRef<any>;

  currentSteps: StepConfig[] = [];
  isVipUser = true; // 模拟VIP用户判断

  ngOnInit() {
    // 基础步骤数组
    this.currentSteps = [
      { title: '基础信息', contentTemplate: this.basicInfoTpl, completed: false, disabled: false },
      { title: '身份验证', contentTemplate: this.authTpl, completed: false, disabled: true },
      { title: '密码设置', contentTemplate: this.pwdSetTpl, completed: false, disabled: true }
    ];

    // 根据用户类型添加额外步骤
    if (this.isVipUser) {
      this.currentSteps.push({
        title: 'VIP配置',
        contentTemplate: this.vipConfigTpl,
        completed: false,
        disabled: true
      });
    }
  }

  handleNext(index: number) {
    // 标记当前步骤为完成,启用下一步
    this.currentSteps[index].completed = true;
    if (index + 1 < this.currentSteps.length) {
      this.currentSteps[index + 1].disabled = false;
    }
  }

  handlePrev(index: number) {
    // 回退时禁用当前步骤,取消上一步完成状态
    this.currentSteps[index].disabled = true;
    this.currentSteps[index - 1].completed = false;
  }

  handleFinish() {
    console.log('流程完成');
    // 后续业务逻辑:提交表单、跳转页面等
  }
}

4. 常见失败原因排查

  • 模板引用时机错误:必须在ngAfterViewInit或ngOnInit(配合static: false)中获取TemplateRef,视图未初始化时无法获取实例
  • 模板标记重复:确保#xxxTpl的命名全局唯一,避免@ViewChild匹配错误
  • ngTemplateOutlet绑定错误:需直接绑定TemplateRef实例,而非字符串或其他类型

内容的提问来源于stack exchange,提问作者PKHDK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 19:01:15