Angular指令中如何判断组件返回Subscription的函数执行完成?
看起来你想把异步操作完成后的通用逻辑封装在指令里,不用在每个组件里重复写——这个需求非常合理,毕竟通用逻辑就该放在通用指令里对吧?
先说说你当前代码的问题:你在指令里订阅appSingleActiveButton这个EventEmitter,只会在点击按钮触发事件时执行回调,但完全没法跟踪组件里save()方法内部异步操作的完成/错误状态。因为EventEmitter只是传递了点击事件,和save()里的HTTP请求生命周期完全脱节。
下面是具体的解决方案,核心是让组件的方法返回Observable(而非直接返回Subscription),然后让指令获取这个Observable并订阅它的生命周期:
1. 调整组件的save()方法,返回Observable
把原来直接订阅HTTP请求并返回Subscription的逻辑,改成返回Observable,用tap(RxJS 6+,对应旧版的do)处理原本在subscribe里的业务逻辑:
import { Component } from '@angular/core'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; import { Http } from '@angular/http'; @Component({ selector: "app-example", templateUrl: "./example.component.html", styleUrls: ["./example.component.scss"], }) export class ExampleComponent { constructor(private http: Http) {} public save(index: number): Observable<any> { const requestUrl = 'http://example.com'; return this.http.get(requestUrl) .pipe( tap(() => console.log(`doing some stuff ${index}`)) ); } }
2. 修改指令,通过Input接收组件的异步方法
将指令的Output改为Input,用来接收组件中返回Observable的方法。点击按钮时,指令调用这个方法并订阅其Observable,从而跟踪异步操作的完成和错误:
import { Directive, HostListener, Input } from '@angular/core'; import { Observable } from 'rxjs'; @Directive({ selector: "[appSingleActiveButton]" }) export class SingleActiveButtonDirective { @Input('appSingleActiveButton') action: () => Observable<any>; constructor() {} @HostListener('click', ['$event']) private onClick(e: any): void { if (!this.action) return; // 调用组件的异步方法,订阅其生命周期执行通用逻辑 this.action().subscribe({ complete: () => { console.log('test: 异步操作成功完成'); // 这里可以加入你的通用处理,比如重置按钮状态、显示提示等 }, error: (err) => { console.log('test: 异步操作失败', err); // 通用错误处理逻辑,比如恢复按钮状态、显示错误提示等 } }); } }
3. 更新组件模板的使用方式
把原来的事件绑定改成Input绑定,传递一个返回Observable的箭头函数(这样就能把循环变量i传递给save()方法了):
<div *ngFor="let btn of btns; let i = index"> <div [appSingleActiveButton]="() => save(i)">test directive</div> </div>
为什么这个方案好用?
- 组件只需要专注于自身业务逻辑,不用额外添加和指令相关的代码;
- 指令完全封装了通用逻辑,不管哪个组件使用这个指令,异步操作完成后都会自动执行通用处理;
- 通过
Observable的生命周期回调,能同时处理成功完成和错误两种情况,覆盖了你的需求。
如果你特别想保留原来的事件绑定写法((appSingleActiveButton)="save(i)"),也可以让组件的save()方法返回Observable,然后通过自定义事件把这个Observable传递回指令,但这种方式相对繁琐,上面的Input方案更简洁,也符合Angular的最佳实践。
内容的提问来源于stack exchange,提问作者Ron

