自定义CdkStepper组件实现步骤懒加载的优化方案咨询
自定义CdkStepper的懒加载方案及实现
更优懒加载方案
不依赖Angular Material的情况下,最优雅的方式是自定义一个结构型指令,结合ng-template实现步骤内容的延迟渲染,彻底摆脱硬编码索引的ngIf写法,同时和CdkStepper的选中状态自动联动。
matTabContent/matStepContent的核心实现逻辑
这类Material指令的本质是延迟模板渲染:
- 指令会捕获宿主
ng-template的引用 - 监听父容器(Tab组/Stepper)的选中状态变化
- 当对应项被选中时,通过
ViewContainerRef将模板内容插入DOM - 默认会缓存已渲染的内容,切换时不会重复销毁重建(可通过配置修改)
自定义懒加载指令的实现步骤
1. 创建LazyStepContent指令
这个指令用来标记需要懒加载的步骤内容,同时暴露模板引用供Stepper组件调用:
import { Directive, TemplateRef } from '@angular/core'; @Directive({ selector: '[lazyStepContent]' }) export class LazyStepContentDirective { constructor(public templateRef: TemplateRef<unknown>) { } }
2. 修改自定义Stepper组件
在MyStepperComponent中,需要获取步骤的懒加载模板、监听选中状态变化,动态渲染内容:
import { Component, ContentChildren, QueryList, ViewChild, ViewContainerRef, AfterViewInit, OnChanges, SimpleChanges } from '@angular/core'; import { CdkStepper, CdkStep } from '@angular/cdk/stepper'; import { LazyStepContentDirective } from './lazy-step-content.directive'; @Component({ selector: 'my-stepper', template: ` <!-- 自定义步进器头部 --> <div class="stepper-header"> <button *ngFor="let step of steps; let i = index" (click)="selectedIndex = i" [class.active]="selectedIndex === i" > 步骤 {{i + 1}} </button> </div> <!-- 步骤内容容器 --> <div class="stepper-content"> <ng-container #contentContainer></ng-container> </div> `, styles: [` .stepper-header { display: flex; gap: 1rem; margin-bottom: 1rem; } .stepper-header button { padding: 0.5rem 1rem; border: none; background: #eee; cursor: pointer; } .stepper-header button.active { background: #2196F3; color: white; } `], providers: [{ provide: CdkStepper, useExisting: MyStepperComponent }] }) export class MyStepperComponent extends CdkStepper implements AfterViewInit, OnChanges { @ViewChild('contentContainer', { read: ViewContainerRef }) contentContainer!: ViewContainerRef; @ContentChildren(CdkStep) steps!: QueryList<CdkStep>; @ContentChildren(LazyStepContentDirective) lazyContents!: QueryList<LazyStepContentDirective>; ngAfterViewInit(): void { this.renderActiveStep(); this.selectedIndexChange.subscribe(() => this.renderActiveStep()); } ngOnChanges(changes: SimpleChanges): void { if (changes['selectedIndex']) { this.renderActiveStep(); } } private renderActiveStep(): void { if (!this.contentContainer || this.selectedIndex < 0 || this.selectedIndex >= this.lazyContents.length) return; this.contentContainer.clear(); const activeContent = this.lazyContents.get(this.selectedIndex); if (activeContent) { this.contentContainer.createEmbeddedView(activeContent.templateRef); } } }
3. 在模板中使用懒加载指令
用ng-template包裹步骤内容,并添加lazyStepContent标记:
<my-stepper> <cdk-step> <ng-template lazyStepContent> <component-one></component-one> </ng-template> </cdk-step> <cdk-step> <ng-template lazyStepContent> <component-two></component-two> </ng-template> </cdk-step> </my-stepper>
4. 可选:缓存已加载内容
如果希望切换步骤时保留已加载的组件状态,可以添加缓存逻辑:
private cachedViews = new Map<number, any>(); private renderActiveStep(): void { if (!this.contentContainer || this.selectedIndex < 0 || this.selectedIndex >= this.lazyContents.length) return; this.contentContainer.clear(); if (this.cachedViews.has(this.selectedIndex)) { this.contentContainer.insert(this.cachedViews.get(this.selectedIndex)); return; } const activeContent = this.lazyContents.get(this.selectedIndex); if (activeContent) { const viewRef = this.contentContainer.createEmbeddedView(activeContent.templateRef); this.cachedViews.set(this.selectedIndex, viewRef); } }
内容的提问来源于stack exchange,提问作者Mark Durkot
相关产品推荐
相关产品推荐

