了解了业务需求后,我们结合 rxjs 操作符来控制 input 框实现上述功能
模板视图 test.component.html 代码如下:
<div class="l-widget-notice-alarmCode">
<input
nz-input
placeholder="输入告警编码"
复制代码
接下来,我们使用 @ViewChild 属性装饰器,从模板视图中获取匹配的元素后,
通过 fromEvent 将一个该元素上的事件转化为一个Observable:
export class NoticeOverviewComponent implements OnInit, OnDestroy, AfterViewInit {
@ViewChild('noticeInput', {static: true}) noticeInput: ElementRef;
ngAfterViewInit() {
this.bindNoticeInputEvent();
private bindNoticeInputEvent(): void {
const noticeInputEvent$ = fromEvent(this.noticeInput.nativeElement, 'keyup');
复制代码
接下来,我们通过 Pipe 管道操作符来操作事件流:
export class NoticeOverviewComponent implements OnInit, OnDestroy, AfterViewInit {
@ViewChild('noticeInput', {static: true}) noticeInput: ElementRef;
ngAfterViewInit() {
this.bindNoticeInputEvent();
private bindNoticeInputEvent(): void {
const noticeInputEvent$ = fromEvent(
this.noticeInput.nativeElement,
'keyup'
noticeInputEvent$.pipe(
debounceTime(300),
filter((event: KeyboardEvent) =>
!(event.keyCode >= 37 && event.keyCode <= 40)
pluck('target', 'value'),
).subscribe(this.loadData);
public loadData(value: string): void {
复制代码
上面的代码中,我们在 pipe 管道中,使用 debounceTime 操作符,舍弃掉在两次输出之间小于指定时间的发出值来完成防抖处理,
通过 filter 操作符过滤符合业务需求的发出值。
最后,我们通过 pluck 操作符来取得发出对象嵌套属性,即 event.value 属性来获取用户的输入值。
由于 Observable 是惰性的,我们需要主动去触发这个函数来获取这个值。
关于 Observable 的介绍可以 参考
Angular - Observable 概述
为了避免订阅操作可能会导致的内存泄漏,我们的请求方法还需要做取消订阅的处理。
由于 Observable 也是一种基于发布、订阅模式的推送体系,在某个时间点,我们需要执行取消订阅操作来释放系统的内存。否则,应用程序可能会出现内存泄露的情况。
Observable 订阅之后会返回一个
subscription
对象,通过调用 subscription 的 unsubscribe 方法来取消当前 Observer 的订阅,关于取消订阅,可以使用标准的模式来取消订阅:
export class NoticeOverviewComponent implements OnInit, OnDestroy, AfterViewInit {
@ViewChild('noticeInput', {static: true}) noticeInput: ElementRef;
noticeInputSubscription: Subscription;
ngAfterViewInit() {
this.bindNoticeInputEvent();
OnDestroy() {
this.noticeInputSubscription.unsubscribe();
private bindNoticeInputEvent(): void {
const noticeInputEvent$ = fromEvent(
this.noticeInput.nativeElement,
'keyup'
this.noticeInputSubscription = noticeInputEvent$.pipe(
debounceTime(300),
filter((event: KeyboardEvent) =>
!(event.keyCode >= 37 && event.keyCode <= 40)
pluck('target', 'value'),
).subscribe(this.loadData);
public loadData(value: string): void {
复制代码
但是这种做法过于麻烦,且一个 Subscription 对应一个 subscribe。
我们可以通过 使用 takeUntil 操作符来实现 observable 的自动取消订阅:
export class NoticeOverviewComponent implements OnInit, OnDestroy, AfterViewInit {
@ViewChild('noticeInput', {static: true}) noticeInput: ElementRef;
private unsubscribe: Subject<void> = new Subject<void>();
ngAfterViewInit() {
this.bindNoticeInputEvent();
OnDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete();
private bindNoticeInputEvent(): void {
const noticeInputEvent$ = fromEvent(
this.noticeInput.nativeElement,
'keyup'
noticeInputEvent$.pipe(
takeUntil(this.unsubscribe),
debounceTime(300),
filter((event: KeyboardEvent) =>
!(event.keyCode >= 37 && event.keyCode <= 40)
pluck('target', 'value'),
).subscribe(this.loadData);
public loadData(value: string): void {
复制代码
takeUntil 接受一个 observable ,当接受的 observable 发出值的时候,源 observable 便自动完成了,利用这种机制不仅可以对单个订阅进行取消,整个组件中都可以利用同一个
unsubscribe: Subject<void>
对象来取消订阅,因为我们使用了 Subject,这是一种
多播
的模式
这种机制也是 Angular 中组件销毁时采用的取消订阅模式的基础
大多数时候,我们可以在 Pipe 最上层来设置 takeUntil 来处理订阅,但是在部分
高阶流
中,订阅者所订阅的 observable 可能是由其他流返回,这个过程也是惰性的,因此如果此时在最上方设置 takeUntil 也极有可能导致内容泄漏的问题。
takeUntil 在一些其他场景中,也有可能会引发一些问题,可以通过 配置 rxjs-tslint-rules 中的 rxjs-no-unsafe-takeuntil 规则来确保 takeUntil 的位置放置正确。在这个业务中,我们在最上方设置 takeUntil 就足够了。
感谢您的阅读~