Angular 订阅组件中的一个可观察到的两次';行不通
我有几个组件订阅了我的数据服务,它们都工作得很好。但在我的一个组件中,我尝试订阅了两次(在ngOnInit和ngAfterViewInit中),但这不起作用。以下是组件:Angular 订阅组件中的一个可观察到的两次';行不通,angular,rxjs,observable,angular5,rxjs5,Angular,Rxjs,Observable,Angular5,Rxjs5,我有几个组件订阅了我的数据服务,它们都工作得很好。但在我的一个组件中,我尝试订阅了两次(在ngOnInit和ngAfterViewInit中),但这不起作用。以下是组件: ngOnInit() { this.dataService.data$.pipe(first()).subscribe(subscribeToData => { this.title = this.dataService.getData("...");
ngOnInit() {
this.dataService.data$.pipe(first()).subscribe(subscribeToData => {
this.title = this.dataService.getData("...");
this.anotherService.getData
.subscribe(another => {
this.data = data;
},
...
});
}
ngAfterViewInit() {
this.dataService.data$.pipe(first()).subscribe(subscribeToData => {
let options = {
data: {
}
...
{
title: this.dataService.getData("...");
},
...
};
...
});
}
如果我从ngOnInit中删除subscribe,那么ngAfterViewInit可以正常工作,否则就会失败。那么,有没有一种方法可以同时从同一个组件中订阅两次或更多次呢
以下是数据服务:
private dataSource = new ReplaySubject(1);
data$ = this.dataSource.asObservable();
loadData(... : void) {
if (sessionStorage["data"] == null {
this.http.request(...)
.map((response: Response) => response.json()).subscribe(data => {
...
sessionStorage.setItem("data", JSON.stringify(this.data));
this.dataSource.next(this.data);
...
});
} else {
this.dataSource.next(this.data);
}
}
getData(... : string){
...
}
在您的代码中使用双订阅没有问题-可能您正面临一些异步代码问题。浏览器速度足够快,可以快速浏览组件的生命周期,并快速调用
ngOnInit
和ngAfterViewInit
。它们几乎同时执行,速度极快,肯定比http调用快。在这种情况下,在您的ngOnInit
的subscribe中,您可能会在ngAfterViewInit
之后执行另一个调用(尽管我不确定)
下面的示例显示了在单个组件中双订阅的工作原理:
尝试重构逻辑,使其更加连续:如果必须在完成ngOnInit
中的所有异步代码后执行ngAfterViewInit
,请将ngOnInit
链的结果存储在变量中的某个位置;如果您的ngAfterViewInit
不关心ngOnInit
,请尽量避免访问相同的变量,尤其是this.data
还应尽量避免嵌套的subscribe
s-它们可以替换为switchMap
/flatMap
:
ngOnInit() {
this.dataService.data$.pipe(
first(),
tap(data => this.title = this.dataService.getData(data)), // note that this should be synchronous
switchMap(data => {
// another asynchronous call here
return this.anotherService.getData(data)
})
).subscribe(finalData => {
this.data = finalData
}
要重构要在ngOnInit
之后执行的ngAfterViewInit
,请执行以下操作:
onInitData$: Observable<any>;
ngOnInit() {
this.onInitData$ = this.dataService.data$.pipe(
first(),
tap(data => this.title = this.dataService.getData(data)), // note that this should be synchronous
switchMap(data => {
// another asynchronous call here
return this.anotherService.getData(data)
}),
shareReplay(1) // shareReplay(1) is important to avoid doing double http requests per subscribe
);
this.onInitData$.subscribe(data => console.log('data from ngOnInit', data));
}
ngAfterViewInit() {
this.onInitData$.pipe(switchMap(thatData) => {
// will be executed only AFTER the ngOnInit is done
return this.dataService.data$.pipe(first()).subscribe(subscribeToData => {
let options = {
data: {
}
...
{
title: this.dataService.getData("...");
},
...
};
...
});
}).subscribe(dataFromAfterViewInit => {})
}
onInitData$:可观察;
恩戈尼尼特(){
this.onInitData$=this.dataService.data$.pipe(
第一个(),
点击(data=>this.title=this.dataService.getData(data)),//注意这应该是同步的
开关映射(数据=>{
//这里是另一个异步调用
返回this.anotherService.getData(数据)
}),
shareReplay(1)//shareReplay(1)对于避免每个订阅执行双重http请求非常重要
);
this.onInitData$.subscribe(data=>console.log('data from ngOnInit',data');
}
ngAfterViewInit(){
this.onInitData$.pipe(开关映射(thatData)=>{
//将仅在完成ngOnInit后执行
返回此.dataService.data$.pipe(first()).subscribe(subscribeToData=>{
让选项={
数据:{
}
...
{
标题:this.dataService.getData(“…”);
},
...
};
...
});
}).subscribe(dataFromAfterViewInit=>{})
}
通常,您会想,为什么需要
ngAfterViewInit
?通过在onInit
/afterViewInit
之间拆分这些调用,您希望实现什么?为什么他们访问组件中的相同数据?为什么您需要订阅两次可观测数据?因为您可以在我的代码中看到,我需要在ngOnInit和ngAftweViewInit上使用数据服务中的数据。我是rxjs新手,所以这种方法可能是错误的。如果您知道更好的解决方案,请随时写信给我。ngOnInit()
是第一个生命周期挂钩,因此它总是首先执行。一旦你订阅了ngOnInit中的observable,你就可以将它存储在一个变量中,然后在NgoAfterViewInit中使用它。你能举个例子说明如何做到这一点吗?@NicholasK我该如何将observable存储在一个变量中并重用它?我想你是对的,我得到的错误是:core.js:1449错误:不存在mDatatable元素。在jQuery.fn.init.$.fn.mDatatable(:885:35)的SafeSubscriber.eval[as_next](comp.component.ts:152)的。。。也许我可以把ngOnInit和NgoAfterViewInit放在一起,但我不确定它是否会破坏任何东西。我对您建议的ngOnInit代码做了一些小修改,以使其正常工作。但是ngAfterViewInit的代码不起作用。我不能应用它。我试着做了几次改变,但都没有效果。你确定它是正确的吗?当我试图编辑代码时,会出现诸如“找不到名称”thatData、“没有重载与此调用匹配…”等错误。我通过在ngAfterViewInit中添加setTimeOut解决了此问题。@Fth您不应该按原样复制我的代码“因为这只是一个例子。您必须将该数据
变量更改为您在组件中拥有的变量(或者为我提供一个合适的stackblitz示例来修复您的代码)在nAfterViewInit
中设置超时
不是正确的方法-您应该尝试避免它