*ngFor DOM 回调后不刷新

*ngFor DOM not refreshing after callback

我在使用 ASP.NET Core、Angular 5 和 SignalR 开发应用程序时遇到了问题。

作为测试,我基于这个示例制作了一个简单的聊天应用程序:https://codingblast.com/asp-net-core-signalr-chat-angular/
但是,当我从

修改 chat.component.ts 文件时
this.hubConnection.on('sendToAll', (nickname: string, message: string) => { this.messages.push(new Message(nickname, message)); });
// or this line
this.hubConnection.on('sendToAll', (nickname: string, message: string) => { this.addMessage(nickname, message); });

this.hubConnection.on('sendToAll', this.addMessage);

DOM 未更新。

我已经一步一步地完成了,我设法弄清楚当回调到达时,它实际上是组件的另一个实例......所以每个字段都是未定义的。

这是完整的组件:

chat.component.html

<section class="card">
    <div class="card-body" *ngIf="messages !== undefined">
        <div *ngFor="let msg of messages">
            <strong>{{msg.nickname}}</strong>: {{msg.message}}
        </div>
    </div>
    <div class="card-footer">
        <form class="form-inline" (ngSubmit)="sendMessage()" #chatForm="ngForm">
            <div class="form-group">
                <label class="sr-only" for="nickname">Nickname</label>
                <input type="text" id="nickname" name="nickname" placeholder="Nickname" [(ngModel)]="nickname" required />
            </div>
            <div class="form-group">
                <label class="sr-only" for="message">Message</label>
                <input type="text" id="message" name="message" placeholder="Your message" [(ngModel)]="message" required />
            </div>
            <button class="btn btn-sm" type="submit" id="sendmessage" [disabled]="!chatForm.valid">Send</button>
        </form>
    </div>
</section>

chat.component.ts

import { Component, OnInit } from '@angular/core';
import { HubConnection } from '@aspnet/signalr';
import { Message } from '../../models/message';

@Component({
    selector: 'app-chat',
    templateUrl: './chat.component.html',
    styleUrls: ['./chat.component.css']
})
export class ChatComponent implements OnInit {
    private hubConnection: HubConnection;

    public nickname: string;
    public message: string;

    public messages: Message[];

    constructor() {
        this.messages = [];
    }

    ngOnInit() {
        this.hubConnection = new HubConnection('http://localhost:55233/chat');

        this.hubConnection.start().then(() => console.log('connection started')).catch(err => console.error(err));

        this.hubConnection.on('sendToAll', this.addMessage);
    }

    private addMessage(nickname: string, message: string): void {
        // this line is useful to avoid undefined error
        // due to the callback pointing to another instance of this component...
        if (this.messages === undefined) {
            this.messages = [];
        }

        this.messages.push(new Message(nickname, message));
    }

    public sendMessage(): void {
        this.hubConnection
            .invoke('sendToAll', this.nickname, this.message)
            .catch(err => console.error(err));
    }
}

这是我试过的方法:

如何在这种情况下使用这一行 this.hubConnection.on('sendToAll', this.addMessage);
感谢您的帮助。

编辑 :
感谢 Supamiu,这是解决方案:

this.hubConnection.on('sendToAll', this.addMessage.bind(this));
this.hubConnection.on('sendToAll', this.addMessage);

相当于

this.hubConnection.on('sendToAll', function(x) { this.addMessage(x); });

在javascript,写函数就得用闭包,或者用胖箭头。这会给

const that = this;
this.hubConnection.on('sendToAll', function(x) { that.addMessage(x); });
// or
this.hubConnection.on('sendToAll', x => { this.addMessage(x); });

Javascript 提供了一种将上下文注入方法调用的工具:bind.

您应该将 this.addMessage 更改为 this.addMessage.bind(this)

请记住,还有其他解决方案,就像 trichetriche 解释的那样,另一个是更改箭头函数的 addMessage,因为箭头函数保持 this 上下文:

private addMessage = (nickname: string, message: string) => {
    // this line is useful to avoid undefined error
    // due to the callback pointing to another instance of this component...
    if (this.messages === undefined) {
        this.messages = [];
    }

    this.messages.push(new Message(nickname, message));
}

通过这样做,您仍然可以使用 this.addMessage 而无需绑定 this 上下文。