当我将函数用于 return 值并导致浏览器崩溃时,为什么 *ngFor 会产生无限循环?
Why *ngFor make infinite loop when I use function to return value and crashing browser?
我不明白为什么 *ngFor 在我对 return 值使用函数时会产生无限循环。
我用Angular8个
聊天视图HTML模板
<div class="row conpeek_message_chat_row" *ngFor="let chatMessage of chatMessages | async ">
<div class="conpeek_chat_message_inner [innerHTML]="getChatMessageText(chatMessage)"></div>
</div>
聊天视图组件
private chatMessages:Observable<Array<any>>;
ngAfterViewInit() {
this.chatMessages = this.chatMessagesService.getMessages();
}
getChatMessageText(chatMessage) {
if (chatMessage.type == 'text' || chatMessage.type == 'info') {
console.log('TYPE TEXT');
return chatMessage.value;
}
if (chatMessage.type == 'image') {
let img_obj = `<div class="conpeek_msg_img_outer"><img src="${chatMessage.value}"></div>`;
return img_obj;
}
if (chatMessage.type == 'file') {
let file_obj = `<a>${chatMessage.filename}</a>`;
return file_obj;
}
}
聊天消息服务
private chatMessages = [];
private chatMessagesStream = new BehaviorSubject(this.chatMessages);
constructor(private dialogInfoService: DialogInfoService) {
this.dialogInfoService.getDialogMembers().subscribe(members => {
this.dialogMembers = members;
});
}
getMessages(): Observable<any> {
return this.chatMessagesStream.asObservable();
}
addMessage(chatMessage) {
if (chatMessage.type != 'info') {
for (let member of this.dialogMembers) {
if (member.member_uuid == chatMessage.sender_dialog_uuid) {
chatMessage.sender_name = member.member_presentation_name;
chatMessage.avatar_id = member.avatar_id;
if (member.member_uuid == this.dialogInfoService.getOwnDialogUUID()) {
chatMessage.sender_type = 'contact';
chatMessage.status = 'sending';
} else {
chatMessage.sender_type = 'consultant';
}
}
}
}
this.chatMessages.push(chatMessage);
this.chatMessagesStream.next(this.chatMessages);
}
初始化此组件并收到第一条消息后,我在控制台
中得到消息的无限循环console.log('TYPE TEXT');
当我使用它请求图像消息类型来下载 blob 时,我的 chrome 浏览器崩溃了。
为什么会发生,如何解决?
已编辑
我在此处找到了 "infinite" 循环的线索。我用最少的附加代码重新创建了该应用程序,并且几乎一切正常。但是当我将 WebSocket 添加到我的应用程序时,当这个 webscoket 做出一些动作(打开连接、发送消息、关闭连接等)时,我的循环进行迭代。无限循环只是一个乒乓消息,我的 websocket 与服务器一起做,以快速检测连接问题。
我在这里展示问题,在 WS 执行每个操作后检查控制台日志,或者通过单击按钮发送 ws 消息。
我阅读了一些相关内容,每个更改检测事件(如按钮单击、http 请求或 websocket 操作)都会导致此循环迭代。
您要更改 getChatMessageText(chatMessage)
中 chatMessages
的值吗?如果是这样,我就是这个问题的根源
尝试将此添加到您的组件中:
chatMessageSubscription: Subscription;
constructor(private chatMessageService: ChatMessagesService) {}
ngOnInit() {
this.chatMessageSubscription = this.chatMessageService.chatMessagesStream .subscribe((messages: data) => {
this.chatMessages = data;
}
}
- 您还没有关闭 class 报价
<div class="conpeek_chat_message_inner [innerHTML]="getChatMessageText(chatMessage)"></div>
- 您的 chatMessagesService return 是可观察的,因此您需要订阅它
// this.chatMessages = this.chatMessagesService.getMessages();
this.chatMessagesService.getMessages().subscribe(messages=>{
this.chatMessages=messages
})
- 因为您有一个带有默认值的行为主题,您可以在初始化视图之前安全地订阅以避免内容检查错误。将
ngAfterViewInit()
替换为 ngOnInit()
我不确定你是如何测试 addMessage
函数的,但有可能是不小心在 observable 本身内,这会导致无限循环。
另外一点,一旦您从服务接收到数据,这将是填充消息文本的好时机,而不是依赖从 html 模板调用的函数。
这是一个最小的工作复制:https://stackblitz.com/edit/angular-xnrupp
我通过将 *ngFor
部分代码放入新组件并添加到此组件装饰器 changeDetection: ChangeDetectionStrategy.OnPush
选项来解决问题。
在 init 上具有停止检测更改的功能 (detach()
)。现在仅当来自服务的新消息到来时循环迭代(我在 subcribe fn 中添加了 detachChanges,如下所示)。
我需要隔离此组件,因为在 Angular 中,每个按钮单击、请求或 WebSocket 消息都会触发整个应用程序中的检测更改,现在我控制它。
ChatMessagesViewComponent
@Component({
selector: 'chat-messages-view',
template: `... *ngFor html here only ...`,
styleUrls: ['./chat-messages-view.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
constructor(private chatMessagesService: ChatMessagesService, private cd: ChangeDetectorRef) {
}
ngOnInit() {
this.chatMessages = this.chatMessagesService.getMessages();
this.cd.detach();
}
ngAfterViewInit() {
this.chatMessagesService.getMessages().subscribe(messages => {
this.cd.detectChanges();
this.scrollMessagesToBottom(true);
});
this.scrollMessagesToBottom(false);
}
我不明白为什么 *ngFor 在我对 return 值使用函数时会产生无限循环。
我用Angular8个
聊天视图HTML模板
<div class="row conpeek_message_chat_row" *ngFor="let chatMessage of chatMessages | async ">
<div class="conpeek_chat_message_inner [innerHTML]="getChatMessageText(chatMessage)"></div>
</div>
聊天视图组件
private chatMessages:Observable<Array<any>>;
ngAfterViewInit() {
this.chatMessages = this.chatMessagesService.getMessages();
}
getChatMessageText(chatMessage) {
if (chatMessage.type == 'text' || chatMessage.type == 'info') {
console.log('TYPE TEXT');
return chatMessage.value;
}
if (chatMessage.type == 'image') {
let img_obj = `<div class="conpeek_msg_img_outer"><img src="${chatMessage.value}"></div>`;
return img_obj;
}
if (chatMessage.type == 'file') {
let file_obj = `<a>${chatMessage.filename}</a>`;
return file_obj;
}
}
聊天消息服务
private chatMessages = [];
private chatMessagesStream = new BehaviorSubject(this.chatMessages);
constructor(private dialogInfoService: DialogInfoService) {
this.dialogInfoService.getDialogMembers().subscribe(members => {
this.dialogMembers = members;
});
}
getMessages(): Observable<any> {
return this.chatMessagesStream.asObservable();
}
addMessage(chatMessage) {
if (chatMessage.type != 'info') {
for (let member of this.dialogMembers) {
if (member.member_uuid == chatMessage.sender_dialog_uuid) {
chatMessage.sender_name = member.member_presentation_name;
chatMessage.avatar_id = member.avatar_id;
if (member.member_uuid == this.dialogInfoService.getOwnDialogUUID()) {
chatMessage.sender_type = 'contact';
chatMessage.status = 'sending';
} else {
chatMessage.sender_type = 'consultant';
}
}
}
}
this.chatMessages.push(chatMessage);
this.chatMessagesStream.next(this.chatMessages);
}
初始化此组件并收到第一条消息后,我在控制台
中得到消息的无限循环console.log('TYPE TEXT');
当我使用它请求图像消息类型来下载 blob 时,我的 chrome 浏览器崩溃了。
为什么会发生,如何解决?
已编辑 我在此处找到了 "infinite" 循环的线索。我用最少的附加代码重新创建了该应用程序,并且几乎一切正常。但是当我将 WebSocket 添加到我的应用程序时,当这个 webscoket 做出一些动作(打开连接、发送消息、关闭连接等)时,我的循环进行迭代。无限循环只是一个乒乓消息,我的 websocket 与服务器一起做,以快速检测连接问题。
我在这里展示问题,在 WS 执行每个操作后检查控制台日志,或者通过单击按钮发送 ws 消息。
我阅读了一些相关内容,每个更改检测事件(如按钮单击、http 请求或 websocket 操作)都会导致此循环迭代。
您要更改 getChatMessageText(chatMessage)
中 chatMessages
的值吗?如果是这样,我就是这个问题的根源
尝试将此添加到您的组件中:
chatMessageSubscription: Subscription;
constructor(private chatMessageService: ChatMessagesService) {}
ngOnInit() {
this.chatMessageSubscription = this.chatMessageService.chatMessagesStream .subscribe((messages: data) => {
this.chatMessages = data;
}
}
- 您还没有关闭 class 报价
<div class="conpeek_chat_message_inner [innerHTML]="getChatMessageText(chatMessage)"></div>
- 您的 chatMessagesService return 是可观察的,因此您需要订阅它
// this.chatMessages = this.chatMessagesService.getMessages();
this.chatMessagesService.getMessages().subscribe(messages=>{
this.chatMessages=messages
})
- 因为您有一个带有默认值的行为主题,您可以在初始化视图之前安全地订阅以避免内容检查错误。将
ngAfterViewInit()
替换为ngOnInit()
我不确定你是如何测试 addMessage
函数的,但有可能是不小心在 observable 本身内,这会导致无限循环。
另外一点,一旦您从服务接收到数据,这将是填充消息文本的好时机,而不是依赖从 html 模板调用的函数。
这是一个最小的工作复制:https://stackblitz.com/edit/angular-xnrupp
我通过将 *ngFor
部分代码放入新组件并添加到此组件装饰器 changeDetection: ChangeDetectionStrategy.OnPush
选项来解决问题。
在 init 上具有停止检测更改的功能 (detach()
)。现在仅当来自服务的新消息到来时循环迭代(我在 subcribe fn 中添加了 detachChanges,如下所示)。
我需要隔离此组件,因为在 Angular 中,每个按钮单击、请求或 WebSocket 消息都会触发整个应用程序中的检测更改,现在我控制它。
ChatMessagesViewComponent
@Component({
selector: 'chat-messages-view',
template: `... *ngFor html here only ...`,
styleUrls: ['./chat-messages-view.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
constructor(private chatMessagesService: ChatMessagesService, private cd: ChangeDetectorRef) {
}
ngOnInit() {
this.chatMessages = this.chatMessagesService.getMessages();
this.cd.detach();
}
ngAfterViewInit() {
this.chatMessagesService.getMessages().subscribe(messages => {
this.cd.detectChanges();
this.scrollMessagesToBottom(true);
});
this.scrollMessagesToBottom(false);
}