如何在Angular父组件中控制多个TypeOut指令的执行顺序?
解决TypeOut指令顺序执行的问题
我来帮你搞定这个文本输出顺序控制的需求!核心思路是用共享服务来统一管理所有TypeOut指令实例,然后父组件通过服务来按顺序触发它们的write方法。下面是一步步的实现方案:
第一步:重构TypeOut服务
我们需要把服务改成一个指令注册中心,负责收集所有指令、排序,并提供按顺序执行的方法:
import { Injectable } from '@angular/core'; import { TypeOutDirective } from './type-out.directive'; @Injectable({ providedIn: 'root' }) export class TypeOutService { private typeOutDirectives: TypeOutDirective[] = []; // 注册指令实例 registerDirective(directive: TypeOutDirective): void { this.typeOutDirectives.push(directive); // 按typeOrder排序,确保顺序正确 this.typeOutDirectives.sort((a, b) => a.order - b.order); } // 按顺序执行所有指令的write方法 async executeInOrder(): Promise<void> { for (const directive of this.typeOutDirectives) { await directive.write(); // 等待当前指令完成后再执行下一个 } } // 可选:清空注册列表(比如组件销毁时调用) clearDirectives(): void { this.typeOutDirectives = []; } }
第二步:修改TypeOut指令
去掉原来ngAfterViewInit里自动执行的write(),改为注册到服务,并在完成时通知(如果需要的话):
import { Directive, Input, ElementRef, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core'; import { delay, asyncForEach } from '../async'; import { TypeOutService } from './type-out.service'; @Directive({ selector: '[typeOut]' }) export class TypeOutDirective implements OnInit, OnDestroy { private dom; @Input('typeOut') text: string; @Input('typeClump') clump: number = 0; // 设置默认值 @Input('typeOrder') order: number = 0; // 默认顺序0 @Input('typeDelay') delay: number = 0; // 默认延迟0 @Output() finished = new EventEmitter<void>(); // 完成事件 typeFinished: boolean = false; charArr: string[] = []; constructor( el: ElementRef, private typeOutService: TypeOutService // 注入服务 ) { this.dom = el.nativeElement; this.dom.innerHTML = ''; } ngOnInit(): void { this.charArr = this.text.split(''); // 注册到服务 this.typeOutService.registerDirective(this); } ngOnDestroy(): void { // 可选:组件销毁时从服务移除,避免内存泄漏 // 这里可以扩展服务的remove方法,不过简单场景下clearDirectives在父组件销毁时调用也可 } write = async () => { if (this.typeFinished) return; // 避免重复执行 this.typeFinished = false; this.dom.innerHTML = ''; // 执行前清空内容 let clump = { max: this.clump, current: 0 } await asyncForEach(this.charArr, async (char) => { if (!this.typeFinished) { if (clump.current >= clump.max) { clump.current = 0; await delay(this.delay); } clump.current++; this.dom.innerHTML += char; } }); this.typeFinished = true; this.finished.emit(); // 触发完成事件 } }
第三步:修改父组件(app-landing)
在父组件中注入服务,在合适的时机(比如ngAfterViewInit)调用executeInOrder来启动顺序执行:
// landing.component.ts import { Component, OnInit, AfterViewInit, OnDestroy } from '@angular/core'; import { TypeOutService } from './type-out.service'; @Component({ selector: 'app-landing', templateUrl: './landing.component.html' }) export class LandingComponent implements AfterViewInit, OnDestroy { title = { ascii: '你的标题ASCII', string: '你的标题' }; subtitle = { ascii: '你的副标题ASCII', string: '你的副标题' }; portrait = { ascii: '你的肖像ASCII', string: '你的肖像' }; constructor(private typeOutService: TypeOutService) {} ngAfterViewInit(): void { // 视图初始化完成后,按顺序执行所有TypeOut指令 this.typeOutService.executeInOrder(); } ngOnDestroy(): void { // 组件销毁时清空服务里的指令列表 this.typeOutService.clearDirectives(); } }
对应的模板部分保持不变,注意typeOrder要设置正确的顺序值,比如:
<h1 class="fontSize1"> <p style="display:none;">{{title.string}}</p> <strong> <pre typeOut="{{title.ascii}}" typeClump="10" typeOrder="1" typeDelay="0.1"></pre> </strong> </h1> <h2 class="fontSize1"> <p style="display:none;">{{subtitle.string}}</p> <pre typeOut="{{subtitle.ascii}}" typeOrder="2" typeDelay="1"></pre> </h2> <aside class="portrait"> <pre class="fontSize2" typeOut="{{portrait.ascii}}" typeClump="10" typeOrder="3" typeDelay="0.1"></pre> </aside>
关键说明
- 服务的作用是集中管理所有指令实例,通过
registerDirective收集后,按typeOrder排序,确保执行顺序正确。 - 指令不再自动执行
write(),而是等待父组件通过服务触发,这样完全由父组件控制执行时机和顺序。 - 如果需要在某个指令完成后做额外操作,可以订阅指令的
finished事件,或者在服务的executeInOrder中处理。 - 记得在父组件销毁时调用
clearDirectives,避免内存泄漏。
这样改造后,你就能完美控制所有TypeOut指令的文本输出顺序啦!
内容的提问来源于stack exchange,提问作者UC3D
相关产品推荐
相关产品推荐

