Brooke Clonts
Brooke Clonts

Reputation: 465

Angular2 Service running http method only once

I have a service that requests info from the server. I only need to run the request once and make it available to every component. So far I have:

@Injectable()
export class ProductService {
    private products: Observable<IProduct[]>;
    private _productUrl = 'app/api/products2.json';

    constructor(private _http: Http) {
        console.log(this.products);
        this.products = this.setProducts();
    }

    setProducts(): Observable<IProduct[]> {
        return this._http.get(this._productUrl)
            .map((response: Response) => <IProduct[]>response.json())
            .do(data => console.log('A: ' + JSON.stringify(data)))
            .catch(this.handleError);
    }

    getProducts(): Observable<IProduct[]> {
        return this.products;
    }

    private handleError(error: Response) {
        return Observable.throw(error.json().error || 'Server error');
    }
}

When the route changes, it runs the constructor setProducts line again. this.products is always undefined when it's logged so doing an if statement doesn't work. What can I change to make sure this http request isn't running when the service should already have that info?

Thanks!

Upvotes: 1

Views: 2701

Answers (3)

Brooke Clonts
Brooke Clonts

Reputation: 465

Ok so @rashfmnb you were along the right lines. I needed to bootstrap it and I needed to add it as a constructor in my child components, rather than a provider. I was creating new instances of it.

Bootstrap: bootstrap(AppComponent, [HTTP_PROVIDERS, provideRouter(AppRoutes), [ProductService, otherService, anotherService, etc.]

Upvotes: 0

rashfmnb
rashfmnb

Reputation: 10538

do this in your bootstrap

it's because each time a new instance of service is created for each component

import {ProductService } from './app/services/product.service'; //put your path not mine
import { provide } from '@angular/core';
import {ReflectiveInjector} from '@angular/core';

let injector = ReflectiveInjector.resolveAndCreate(HTTP_PROVIDERS);
let http = injector.get(Http);
let prodService = new ProductService (http);    

bootstrap(AppComponent, [ 
  [provide(ProductService ,{useValue:prodService})]
]).catch(err => console.error(err));

Upvotes: 1

Bernardo Pacheco
Bernardo Pacheco

Reputation: 1445

Create a private property to cache the result. The first component to use your service will fire a server reques, but the subsequent requests will get the cached products.

@Injectable()
export class ProductService {
    private _productUrl = 'app/api/products2.json';
    private products: IProduct[];

    constructor(private _http: Http) {}

    getProducts(): Observable<IProduct[]> {
       if (this.products) {
          return Observable.of(this.products);
       } else {
          return this._http.get(this._productUrl)
            .map((response: Response) => <IProduct[]>response.json())
            .do(data => console.log('A: ' + JSON.stringify(data)))
            .catch(this.handleError);
       }
    }

    private handleError(error: Response) {
        return Observable.throw(error.json().error || 'Server error');
    }
}

Upvotes: 0

Related Questions