为什么Angular组件只有在点击后才会更新?

3

我对Angular还很生疏,因此可能会错过一些基本的东西。我已经查看了Angular文档和谷歌搜索,但仍然不清楚为什么我的组件仅在单击后更新UI?

我的场景是这样的:我想有一个名为NotificationComponent的共享组件和一个名为NotificationService的共享服务。由于可能会出现多个错误,我的NotificationComponent应该显示所有这些消息。除此之外,我还有一个http错误拦截器和自定义错误处理。

发生的情况是:我收到了3个https错误,拦截器捕获了全部3个错误,自定义错误处理也处理了全部3个错误,通知服务创建了全部3个错误,但通知组件只自动呈现1个(有时是2个)错误。当我单击UI上的任何位置时,剩余的消息就会显示出来。这是为什么呢?

层次结构:AppModule导入SharedModule[包含通知服务和组件]。通知组件选择器位于app.component.html内部。

组件:

@Component({
  selector: 'app-notification',
  templateUrl: './notification.component.html',
  styleUrls: ['./notification.component.scss']
})
export class NotificationComponent implements OnInit, OnDestroy  {
  
  messages: Message[] = [];
  notificationSubscription: Subscription;

  constructor(private notificationService: NotificationService) { }

  ngOnInit() {
    this.notificationSubscription = this.notificationService.onNotify()
      .subscribe(msg => {
        this.messages.push(msg);
      });
  }

  ngOnDestroy() {
    // unsubscribe to avoid memory leaks
    this.notificationSubscription.unsubscribe();
  }
}

HTML:
<div *ngFor = "let msg of messages" role="alert">
    {{msg.detail}}
</div>

服务:

@Injectable({
  providedIn: 'root'
})

export class NotificationService {

    messages: Message[];
    private subject = new Subject<Message>();

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

    onNotify(): Observable<Message> {
      return this.subject.asObservable();
    }
  
    error(title: string, detail: string): void {
      this.notify({type: 'error', title: title, detail: detail});
    }

    notify(message: Message) {
      this.subject.next(message);
    }
}

自定义错误处理程序:

@Injectable()
export class CustomErrorHandler implements ErrorHandler {

    constructor(@Inject(NotificationService) 
        private notificationService: NotificationService) {
    }

    handleError(error: any): void {
        
        this.notificationService.error(error.title, error.detail);
    }

}

注意:我并没有受限于任何具体的实现方案,所以必要时可以更改方法。


我更新了代码。我相信我的组件没有任何装饰器。该组件是 SharedModule 的一部分,被 AppModule 导入。选择器放置在 app.component.html 中。 - Tiago B
尝试在您的服务中导入 ngZone 并修改 notify(message: Message) { this.ngZone.run(() => { this.subject.next(message); }); } - N.F.
1
嗨,我认为你遇到了与这个问题相同的问题 https://dev59.com/opfga4cB1Zd3GeqPDvls ,根本原因是当错误发生时,变更检测不会执行。 - pennyliangliping
是的,这个人和我一样遇到了同样的问题。由于那里被接受的答案相当老旧,我相信@prahbh的方法使用了更好的类来进行注入,因此我将保持我的问题活跃,以便更新解决方案。 - Tiago B
1个回答

5
在你的 NotificationComponent 类中,在 constructor 中注入 ChangeDetectorRef,并明确更新 messages 数组,这样 Angular 就可以推断属性已更改,从而更新 UI。
  constructor(
    private readonly changeDetector: ChangeDetectorRef,
    private readonly notificationService: NotificationService
  ) {}

  public ngOnInit(): void {
    this.notificationSubscription = this.notificationService
      .onNotify()
      .subscribe({
        next: (msg) => {
        // Don't mutate existing array, Angular can't infer if it should update UI
        this.messages = [...this.messages, msg];
        // explicitly update UI if the above approach doesn't work
        this.changeDetector.detectChanges();
      }
    });
  }

我必须使用detectChanges()方法,它起作用了!谢谢。 - Tiago B

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接