在我的Angular6应用程序中,我遇到了一个使用async 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>
发布于 2018-10-06 00:50:29
经过研究,我用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);
});
};
我只是想知道这个解决方案是否可以被认为是完全反应的?我找不到任何其他的方法来让它工作。
发布于 2018-10-05 06:02:50
这是一个同步性问题。Observable.create
同步运行并创建一个可观察对象。来自您的EventSource的回调确实被调用了,但是已经太晚了,无法实现创建的observable。
解决方案是使用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);
}
};
}
}
发布于 2018-10-05 12:42:22
获得服务结果后,在使用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语言中,您可以简单地迭代answers
<li *ngFor="let answer of answers">{{answer.answer}}</li>
https://stackoverflow.com/questions/52655177
复制相似问题