【问题标题】:Observable updates UI only onceObservable 只更新一次 UI
【发布时间】:2020-09-16 21:21:42
【问题描述】:

我对 Angular (10) 还很陌生,并试图掌握 Observables 的概念。我有一个服务和一个组件,服务用参与者填充一个数组,组件应该显示它们

服务

export class MercureService {

  private _participants = new BehaviorSubject<ParticipantDTO[]>([]);
  private participantList: Array<ParticipantDTO> = [];

  constructor() {
  }

  get participants$(): Observable<Array<ParticipantDTO>> {
    return this._participants.asObservable();
  }

  admin_topic(topic: AdminTopic): void {

    const url = new URL(environment.mercure);
    url.searchParams.append('topic', `${topic.sessionUuid}_${topic.userUuid}`);

    const eventSource = new EventSource(url.toString());
    eventSource.onmessage = event => {
      const json = JSON.parse(event.data);
      console.log('NEW EVENT');
      // ...
      if (json.event === BackendEvents.NEW_PARTICIPANT) {
        this.participantList.push({voter: json.data.voter, voterUuid: json.data.voterUuid, vote: '0'});
        this._participants.next(this.participantList);
      }
    };
  }

Component.ts

export class StartSessionComponent implements OnInit, OnDestroy {
  // ...
  unsubscribe$: Subject<void> = new Subject<void>();
  participantList: ParticipantDTO[];

  constructor(
    // ...
    public mercure: MercureService
  ) {}

  ngOnInit(): void {
    this.mercure.participants$
      .pipe(takeUntil(this.unsubscribe$))
      .subscribe((data) => {
        this.participantList = data;
      });

    this.mercure.admin_topic({sessionUuid: '123', userUuid: '456'});
  }

  ngOnDestroy(): void {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
  }

Component.html

...
  <div *ngFor="let participant of this.mercure.participants$ | async">
    <p>{{participant.voter}} - Vote ({{participant.vote}})</p>
  </div>
...

所以我没有发送消息,它被 EventSource 接收,console

NEW EVENT

并且 UI 得到更新(添加一个新的 &lt;p&gt;WHATEVER NAME - VOTE XXX&lt;/p&gt;)。但是,当我从服务器发送第二条消息时,我得到了

NEW EVENT

再次,但 UI 没有更新。我怀疑我对 Observable 做错了什么,有人可以帮忙吗?

【问题讨论】:

    标签: angular rxjs observable mercure


    【解决方案1】:

    这是一种预期的行为,因为this.participantList 引用了一个已由主题存储的值(因为引用尚未更改),您可能希望每次都扩展您的数组以创建一个新数组更新其值:

    this._participants.next(....this.participantList);
    

    【讨论】:

      【解决方案2】:

      问题是 EventSource 事件是在 Angular 之外发出的,因此您的 eventSource.onmessage 内部发生的任何事情都不会正确更新 UI。这就是为什么你需要在 NgZone 的帮助下将 onmessage 中发生的任何事情包装起来,以便在 Angular 中运行。

      查看示例:

        constructor(
          private zone: NgZone
        ) { }
      
        admin_topic(topic: AdminTopic): void {
          const url = new URL(environment.mercure);
          url.searchParams.append('topic', `${topic.sessionUuid}_${topic.userUuid}`);
      
          const eventSource = new EventSource(url.toString());
          eventSource.onmessage = event => {
            this.zone.run(() => { // This is what you need
              const json = JSON.parse(event.data);
              console.log('NEW EVENT');
              // ...
              if (json.event === BackendEvents.NEW_PARTICIPANT) {
                this.participantList.push({ voter: json.data.voter, voterUuid: json.data.voterUuid, vote: '0' });
                this._participants.next(this.participantList);
              }
            })
          };
        }
      

      另一种解决方案是使用 Event Source 包装器,它会为您做完全相同的事情并让您易于使用。它也会被包裹在 Observable 中,让你有丰富的经验。例如看这篇文章:Event Source wrapped with NgZone and Observable

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-11-09
        • 2023-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-04
        相关资源
        最近更新 更多