Angular 多次调用 EventEmitter
Angular EventEmitter called multiple times
这真的很奇怪,很难解释。
我在我的一些服务中使用了 EventEmitter
,并且我一直在使用它来更改我的视图中的数据。
我遇到了更改路线(通过 links 或通过历史回溯)的问题,它似乎多次触发,因此它打乱了我的逻辑。
所以我在 stackblitz 上创建了一个测试,看看我是否可以重新创建它。
我做了一个简单的服务:
import { Injectable, Output, EventEmitter } from '@angular/core';
@Injectable()
export class ListService {
@Output() listChanged: EventEmitter<any[]> = new EventEmitter<any[]>()
constructor() { }
list() {
this.listChanged.emit([]);
}
}
然后在我的其中一条路线中,我只是这样做:
import { Component, OnInit } from '@angular/core';
import { ListService } from '../list.service';
@Component({
selector: 'app-products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {
count: any[] = []
constructor(
private listService: ListService
) { }
ngOnInit() {
this.listService.listChanged.subscribe(() => {
this.count.push('invoked');
console.log('invoked');
console.log('------');
});
this.listService.list();
}
}
然后我创建了一个像这样的简单模板:
<p>
Invoke should only be called once
</p>
<ul>
<li *ngFor="let item of count">{{ item }}</li>
</ul>
当你在路由之间导航时,它看起来像预期的那样工作(ngFor 应该只有一个项目),但是如果你打开控制台看看, 你会看到每次从一个视图来回移动时,它都会额外触发一次。
- 所以,第一次访问你会看到控制台输出一次。
- 清除控制台并在视图之间移动,您将看到两次控制台输出。
- 清除控制台并在视图之间移动,您将看到控制台输出三次.....
每次交换视图时都会发生这种情况。
这是 stackblitz link 所以你可以自己看。
https://stackblitz.com/edit/angular-sthags
谁能告诉我为什么会这样以及如何阻止它?
这是因为您没有在组件销毁时终止订阅(每次 Products 组件初始化时都会创建新订阅,从而导致内存泄漏)
将订阅分配给 class 变量,使用 ngOnDestroy()
钩子终止订阅。
subsVar: Subscription;
ngOnInit() {
this.subsVar = this.listService.listChanged.subscribe(() => {
this.count.push('invoked');
console.log('invoked');
console.log('------');
});
}
ngOnDestroy() {
if (this.subsVar) {
this.subsVar.unsubscribe()
}
}
https://stackblitz.com/edit/angular-9rvxgv?file=src/app/products/products.component.ts
我有一个简单的方法可以解决你的问题
export class ProductsComponent implements OnInit {
count: any[] = [];
isAlowToSub: boolean = true;
constructor(
private listService: ListService
) { }
ngOnInit() {
this.listService.listChanged.subscribe(() => {
if(this.isAlowToSub == true)
{
this.count.push('invoked');
console.log('invoked');
console.log('------');
this.isAlowToSub = false;
}
});
this.listService.list();
}
}
这真的很奇怪,很难解释。
我在我的一些服务中使用了 EventEmitter
,并且我一直在使用它来更改我的视图中的数据。
我遇到了更改路线(通过 links 或通过历史回溯)的问题,它似乎多次触发,因此它打乱了我的逻辑。
所以我在 stackblitz 上创建了一个测试,看看我是否可以重新创建它。 我做了一个简单的服务:
import { Injectable, Output, EventEmitter } from '@angular/core';
@Injectable()
export class ListService {
@Output() listChanged: EventEmitter<any[]> = new EventEmitter<any[]>()
constructor() { }
list() {
this.listChanged.emit([]);
}
}
然后在我的其中一条路线中,我只是这样做:
import { Component, OnInit } from '@angular/core';
import { ListService } from '../list.service';
@Component({
selector: 'app-products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {
count: any[] = []
constructor(
private listService: ListService
) { }
ngOnInit() {
this.listService.listChanged.subscribe(() => {
this.count.push('invoked');
console.log('invoked');
console.log('------');
});
this.listService.list();
}
}
然后我创建了一个像这样的简单模板:
<p>
Invoke should only be called once
</p>
<ul>
<li *ngFor="let item of count">{{ item }}</li>
</ul>
当你在路由之间导航时,它看起来像预期的那样工作(ngFor 应该只有一个项目),但是如果你打开控制台看看, 你会看到每次从一个视图来回移动时,它都会额外触发一次。
- 所以,第一次访问你会看到控制台输出一次。
- 清除控制台并在视图之间移动,您将看到两次控制台输出。
- 清除控制台并在视图之间移动,您将看到控制台输出三次.....
每次交换视图时都会发生这种情况。 这是 stackblitz link 所以你可以自己看。
https://stackblitz.com/edit/angular-sthags
谁能告诉我为什么会这样以及如何阻止它?
这是因为您没有在组件销毁时终止订阅(每次 Products 组件初始化时都会创建新订阅,从而导致内存泄漏)
将订阅分配给 class 变量,使用 ngOnDestroy()
钩子终止订阅。
subsVar: Subscription;
ngOnInit() {
this.subsVar = this.listService.listChanged.subscribe(() => {
this.count.push('invoked');
console.log('invoked');
console.log('------');
});
}
ngOnDestroy() {
if (this.subsVar) {
this.subsVar.unsubscribe()
}
}
https://stackblitz.com/edit/angular-9rvxgv?file=src/app/products/products.component.ts
我有一个简单的方法可以解决你的问题
export class ProductsComponent implements OnInit {
count: any[] = [];
isAlowToSub: boolean = true;
constructor(
private listService: ListService
) { }
ngOnInit() {
this.listService.listChanged.subscribe(() => {
if(this.isAlowToSub == true)
{
this.count.push('invoked');
console.log('invoked');
console.log('------');
this.isAlowToSub = false;
}
});
this.listService.list();
}
}