Angular 2 http get observable 调用了两次

Posted

技术标签:

【中文标题】Angular 2 http get observable 调用了两次【英文标题】:Angular 2 http get observable called twice 【发布时间】:2017-04-24 08:20:19 【问题描述】:

在 Angular 2 v2.0.1 中,onInit 被调用了两次。 (显然,当它被调用一次时我也做错了,但这不是现在的问题)

这是我的 Plunker:http://plnkr.co/edit/SqAiY3j7ZDlFc8q3I212?p=preview

这是服务代码:

import Injectable from '@angular/core';
import Http, Response from '@angular/http';
import Observable from 'rxjs/Rx';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';

@Injectable()
export class DemoService 


  constructor(private http:Http)  

  // Uses http.get() to load a single JSON file
  getData() 
    return this.http.get('./src/data.json')
      .map((res:Response) => res.json())
      .do(data => console.log(data))
      .subscribe(data => 
        return <PageContent[]>data;
      , error => console.log("there was an error!"));
  


export class PageContent 
  constructor(public _id: string, 
  public tag: string, 
  public title: string, 
  public body?:string, 
  public image?: string) 

...以及使用它的简单组件。

//our root app component
import Component, NgModule, OnInit  from '@angular/core'
import BrowserModule from '@angular/platform-browser'
import  DemoService, PageContent  from './service';
import  HttpModule  from '@angular/http';

@Component(
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello name</h2>
    </div>
    <div *ngFor="let page of pages">
       page.title 
    </div>
  `
)
export class App implements OnInit 
  name:string;
  pages: PageContent[] = [];

  constructor(private _service: DemoService) 
    this.name = 'Angular2'
    this.loadData();  // <-- this is called once
  

  ngOnInit() 
    //this.loadData();  // <-- this is called twice 
  

  loadData()
    this.pages = this._service.getData();
    console.log(this.pages);
  


@NgModule(
  imports: [ BrowserModule, HttpModule ],
  declarations: [ App ],
  providers: [DemoService],
  bootstrap: [ App ]
)
export class AppModule 

免责声明:它出错了,但是当从构造​​函数调用服务方法时,您可以看到它被服务一次,但是当它在 ngOnInit 钩子内时被调用两次。

我的问题是,为什么 OnInit 函数会调用它两次?

更新:所有答案的解决方案:

这是新的服务方式:

getData() 
    return this.http.get('./src/data.json')
        .map((res:Response) => res.json() as PageContent[]);

...这是新的组件方法:

loadData()
    this._service.getData()
        .subscribe(data => this.pages = data);

【问题讨论】:

使用 ngFor 删除模板的一部分并导致应用失败,你会看到它在正常情况下只被调用了一次。 @JBNizet - 但我需要 *ngFor 来显示数据。 【参考方案1】:

您的subscribe 应该放在组件而不是服务中。原因是您的组件订阅了从服务返回的数据,稍后您可以在需要时取消订阅或添加更多控制(例如拒绝)。更改后的代码将如下所示。

在您的组件中:

  ngOnInit() 
    this.loadData();
  



  loadData()
    this._service.getData().subscribe(data => this.pages = data);
  

为您服务:

  getData() 
    return this.http.get('./src/data.json')
      .map((res:Response) => res.json());
  

【讨论】:

我最初尝试过,但没有成功,所以我尝试了一些不同的方法。我把它放回去了,所以它和你的相似,它仍然,1)不返回数据,它说它需要一个可迭代的数组,2)它仍然被调用两次。注意 ngOnInit() 中的“hi”console.log。我让它在 rc 4 中工作。 @KingWilder,我用我在回答中描述的更改修改了你的 plunker,它对我来说很好。看看这个plnkr.co/edit/aLKI3cPthuKmcvU32BgF?p=preview 是的,我根据您的回答和 Soywod 修改了我的 Plunk,并且成功了。【参考方案2】:

this._service.getData() 返回一个主题,而不是一个页面内容列表。你可以改变你的loadData 喜欢:

loadData() 
  this._service.getData().subscribe(data => this.pages = data);
  console.log("Load data !");

并删除getData 方法中的subscribe 部分(来自DemoService)。我刚刚测试了这个,ngOnInit 被调用了一次

【讨论】:

我的评论与我写给 Anthony C 的评论相同。我实际上需要返回一个 PageContent 数组以进行强类型化。我根据 Angular 2 文档更新了 Plunk,但它仍然无法正常工作。仍然不知所措。【参考方案3】:

在英语中,当您订阅流 (Observable) 时,订阅块内的第一个函数内的代码将在该 observable 发出数据时执行。

如果您订阅两次,它将被调用两次,等等

由于您订阅了多次,因此订阅块中的第一个函数(称为下一个函数)将被执行多次。

您应该只在ngOnInit 中订阅一次流。

当您想将数据发送到流上时,您可以为此使用 RXJS 主题,然后使用 RXJS 平面图使主题随后发送到您订阅的流。

【讨论】:

但是我没有看到我在哪里订阅了两次。好的,我在服务中有订阅,但我在组件中也没有订阅。我还需要返回一个 PageContent 对象数组,而不是 Subjects。我根据 A2 文档更新了我的 Plunk,但没有任何变化。【参考方案4】:

好的,我的第二个答案...看看这是否有帮助...

return subject.asObservable().flatMap((emit: any) => 

  return this.http[method](url, emit, this.options)
    .timeout(Config.http.timeout, new Error('timeout'))
    // emit provides access to the data emitted within the callback
    .map((response: any) => 
      return emit, response;
    )
    .map(httpResponseMapCallback)
    .catch((err: any) => 
      return Observable.from([err.message || `$err.status $err.statusText`]);
    );
).publish().refCount();

其中 subject 是您要发送到的 RXJS 主题(使用 subject.next() 方法)

【讨论】:

以上是关于Angular 2 http get observable 调用了两次的主要内容,如果未能解决你的问题,请参考以下文章

Angular 2 路由器使用 Observable 解析

如何在 Angular 2 Http Observable 请求中设置间隔

Angular 2 observable 没有“映射”到模型

Angular 2 - 路由 - CanActivate 与 Observable 一起工作

从 Angular 2 服务创建和返回 Observable

如何在 Angular/RxJS 中合并两个 observable?