Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 http get observable called twice

In Angular 2 v2.0.1 the onInit is called twice. (Obviously I'm also doing something wrong when it's called once, but that's not the issue right now)

Here's my Plunker: http://plnkr.co/edit/SqAiY3j7ZDlFc8q3I212?p=preview

Here's the service code:

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) {}
}

... and the simple component that uses it.

//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 {}

Disclaimer: it's erroring out, but you can see it's being served once when the service method is being called from the constructor, but it gets called twice when it's inside the ngOnInit hook.

My question is, why is it being called twice from the OnInit function?

UPDATE: solution from all answers:

This is the new service method:

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

... and this is the new component method:

loadData(){
    this._service.getData()
        .subscribe(data => this.pages = data);
}
like image 265
King Wilder Avatar asked Dec 08 '16 23:12

King Wilder


People also ask

Why observer method is called twice?

this is happening because you subscribe to the observable twice, and observables execute every time they're subscribed to.

Why subscribe is called twice?

The most common reason for this is that you have called subscribe() multiple times. Every time it is called it adds a new message listener, even if that same listener is already registered for that event; when a message is received, every registered listener will be called with it.

What is observable in HTTP in angular?

Angular makes use of observables as an interface to handle a variety of common asynchronous operations. For example: The HTTP module uses observables to handle AJAX requests and responses. The Router and Forms modules use observables to listen for and respond to user-input events.


2 Answers

Your subscribe should be put in the component instead of the service. Reason being your component is subscribed to the data returned from the service, and later on you can unsubscribe or add more control (such as denounce) if needed. The code will look like this after the changes.

In your component:

  ngOnInit() {
    this.loadData();
  }



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

In you service:

  getData() {
    return this.http.get('./src/data.json')
      .map((res:Response) => res.json());
  }
like image 95
Anthony C Avatar answered Oct 17 '22 05:10

Anthony C


this._service.getData() returns a Subject, not a PageContent list. You could change your loadData like :

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

and remove the subscribe part of your getData method (from DemoService). I've just tested this, and the ngOnInit is called once

like image 37
soywod Avatar answered Oct 17 '22 05:10

soywod