我不明白为什么* ngFor在使用函数返回值时进行无限循环。
我使用Angular 8
聊天视图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浏览器崩溃了。
为什么会发生,以及如何解决?
已编辑 我找到了为什么在这里有“无限”循环的线索。我用最少的附加代码重新创建了该应用程序,并且一切正常。但是,当我将WebSocket添加到我的应用程序中时,并且当此Webscoket进行一些操作(打开连接,发送消息,关闭连接等)时,我的循环进行了迭代。无限循环只是我的Websocket与服务器之间的乒乓消息,用于快速检测连接问题。
在这里,我显示问题,在WS进行每个操作后检查控制台日志,或者通过单击按钮发送ws msg。
我阅读了有关它的内容,每个更改检测事件(如按钮单击,http请求或websocket操作)都会导致此循环迭代。
答案 0 :(得分:1)
<div class="conpeek_chat_message_inner [innerHTML]="getChatMessageText(chatMessage)"></div>
// this.chatMessages = this.chatMessagesService.getMessages();
this.chatMessagesService.getMessages().subscribe(messages=>{
this.chatMessages=messages
})
ngAfterViewInit()
替换为ngOnInit()
我不确定您是如何测试addMessage
函数的,但是在可观察范围内偶然有机会导致无限循环。
另外,一旦从服务接收到数据,这将是填充消息文本的好时机,而不是依赖html模板调用的函数。
这是最小的工作复制品:https://stackblitz.com/edit/angular-xnrupp
答案 1 :(得分:0)
您是否要更改chatMessages
中getChatMessageText(chatMessage)
的值。如果是这样,它就是这个问题的根源
答案 2 :(得分:0)
尝试将其添加到您的组件中
chatMessageSubscription: Subscription;
constructor(private chatMessageService: ChatMessagesService) {}
ngOnInit() {
this.chatMessageSubscription = this.chatMessageService.chatMessagesStream .subscribe((messages: data) => {
this.chatMessages = data;
}
}
答案 3 :(得分:0)
我通过将*ngFor
部分代码放入新组件并添加到该组件装饰器changeDetection: ChangeDetectionStrategy.OnPush
选项中,解决了该问题。
初始化时具有停止检测更改(detach()
)的功能。现在,仅当来自服务的新消息到来时才循环迭代(我在下面的用户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);
}