Angular 订阅组件中的一个可观察到的两次';行不通

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和ngAfterViewInit中),但这不起作用。以下是组件:

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
中设置超时
不是正确的方法-您应该尝试避免它