ng对于不显示来自可观察事件源的数据

时间:2018-10-04 21:09:09

标签: angular rxjs observable ngfor eventsource

在我的Angular6应用程序中,使用异步ngfor显示数据时遇到问题。我期望后端有一些服务器发送事件(只是带有应答字符串字段的对象)。虽然console.log显示服务的答案列表包含答案,但是ngfor不显示任何内容。

这是我的组成部分:

import { Component } from '@angular/core';
import { Answer } from './answer';
import { AnswerReactiveService } from './answer-reactive.service';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-answers',
  templateUrl: './answers.component.html',
  providers: [AnswerReactiveService],
  styleUrls: ['./answers.component.css']
})
export class AnswersComponent {
  answers: Observable<Answer[]>;

  constructor(private answerReactiveService: AnswerReactiveService) {
  }

  requestAnswerStream(): void {
    this.answers = this.answerReactiveService.getAnswerStream();
  }

}

这是服务:

import { Injectable } from '@angular/core';

import { Answer } from './answer';

import { Observable } from 'rxjs';

@Injectable()
export class AnswerReactiveService {

  private answers: Answer[] = [];
  private url: string = 'http://localhost:8080/events/1';

  getAnswerStream(): Observable<Array<Answer>> {
    return Observable.create((observer) => {
      let url = this.url;
      let eventSource = new EventSource(url);
      eventSource.onmessage = (event) => {
        console.log('Received event: ', event);
        const json = JSON.parse(event.data);
        console.log(json);
        this.answers.push(new Answer(json['answer']));
        console.log(this.answers);
        observer.next(this.answers);
      };
      eventSource.onerror = (error) => {
        if (eventSource.readyState === 0) {
          console.log('The stream has been closed by the server.');
          eventSource.close();
          observer.complete();
        } else {
          observer.error('EventSource error: ' + error);
        }
      };
    });
  }
}

和HTML:

<div class="row">
  <div class="col-md-8">
    <p>
      <button (click)="requestAnswerStream()">Gimmie answers</button>
    </p>
    <ul>
      <li *ngFor="let answer of answers | async">{{answer.answer}}</li>
    </ul>
  </div>
</div>

4 个答案:

答案 0 :(得分:0)

这是一个同步问题。 Observable.create同步运行并创建一个可观察的对象。您的EventSource的回调确实得到了调用,但为时已晚,以至于无法创建可观察的结果。

解决方案是使用Subject,它是服务类的成员,并且将一直保留到EventSource回调对其起作用为止。您必须根据组件的构造函数中可观察到的组件来“连接”服务。然后getAnswerStream()变成triggerAnswerStream(),该事件将Eventsource发布事件设置为主题。

类似这样的东西:

@Component({
  selector: 'app-answers',
  templateUrl: './answers.component.html',
  providers: [AnswerReactiveService],
  styleUrls: ['./answers.component.css']
})
export class AnswersComponent {
  answers: Observable<Answer[]>;

  constructor(private answerReactiveService: AnswerReactiveService) {
    this.answers = answerReactiveService.answerStreamSubject.asObservable();
  }

  requestAnswerStream(): void {
    this.answerReactiveService.getAnswerStream();
  }

}

@Injectable()
export class AnswerReactiveService {

  private answers: Answer[] = [];
  private url: string = 'http://localhost:8080/events/1';

  public answerStreamSubject: Subject<Array<Answer>>;

  triggerAnswerStream(): void {
      let url = this.url;
      let eventSource = new EventSource(url);
      eventSource.onmessage = (event) => {
        console.log('Received event: ', event);
        const json = JSON.parse(event.data);
        console.log(json);
        this.answers.push(new Answer(json['answer']));
        console.log(this.answers);
        this.answerStreamSubject.next(this.answers);
      };
      eventSource.onerror = (error) => {
        if (eventSource.readyState === 0) {
          console.log('The stream has been closed by the server.');
          eventSource.close();
          this.answerStreamSubject.complete();
        } else {
          this.answerStreamSubject.error('EventSource error: ' + error);
        }
      };
  }
}

答案 1 :(得分:0)

获得服务结果后,使用 observer.next()

发出值后,使用 observer.complete 完成序列。
eventSource.onmessage = (event) => {
        console.log('Received event: ', event);
        const json = JSON.parse(event.data);
        console.log(json);
        this.answers.push(new Answer(json['answer']));
        console.log(this.answers);
        observer.next(this.answers);
        observer.complete();
      };
   };

内部组件

export class AnswersComponent implements OnInit {
  answers: Observable<Answer[]>;

  constructor(private answerReactiveService: AnswerReactiveService) {
  }

  ngOnInit(): void {
        this.answerReactiveService.getAnswerStream().subscribe((response)=>{
        this.answers=response;
    });
  }
}

然后在HTML中,您可以简单地迭代答案

 <li *ngFor="let answer of answers">{{answer.answer}}</li>

答案 2 :(得分:0)

经过一些研究,我通过使用ngZone获得了结果。我在这里发现了类似的问题:https://blog.octo.com/en/angular-2-sse-and-changes-detection/

现在我的服务代码如下所示,并且可以正常工作:

      eventSource.onmessage = (event) => {
        this.zone.run(() => {
          console.log('Received event: ', event);
          const json = JSON.parse(event.data);
          this.answers.push(new Answer(json['answer']));
          observer.next(this.answers);
        });
      }; 

我只是想知道这种解决方案是否可以视为完全反应性的? 我没有找到其他方法可以完成这项工作。

答案 3 :(得分:0)

尝试使用-addEventListener

其中一个示例对我有用:

@Injectable()
export class EmployeeService {

  public employeesBehavior: BehaviorSubject<Employee>;
  public employeesObservable: Observable<Employee>;

  private URL = 'http://localhost:8080';

  constructor() {
    this.employeesBehavior = new BehaviorSubject(null);
    this.employeesObservable = this.employeesBehavior.asObservable();
  }

  public stream() {
    const streamURL = this.URL + '/stream';
    const eventSource = new EventSource(streamURL);

    eventSource.addEventListener('employees', (event: any) => {
      const employee = JSON.parse(event.data) as Employee;
      this.employeesBehavior.next(employee);
    });
  }
}

投诉人

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

  public employees: Employee[] = [];

  constructor(
    public employeeService: EmployeeService
  ) {

  }

  ngOnInit(): void {
    this.employeeService.stream();

    this.employeeService.employeesObservable.subscribe(e => {
      if (e != null) {
        this.employees.push(e);
      }
    });
  }
}

模板

<div *ngFor="let employee of employees">
     {{employee.name}}
</div>

服务器(春季启动)

@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<Employee>> stream() {
        return employeeService.stream().delayElements(Duration.ofSeconds(1)).map(e ->
            ServerSentEvent.<Employee>builder()
                .id(String.valueOf(e.getId()))
                .event("employees")
                .data(e)
                .build()
        );
    }