1

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!

Brooke Clonts
  • 465
  • 1
  • 10
  • 20

3 Answers3

1

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));
rashfmnb
  • 9,959
  • 4
  • 33
  • 44
0

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');
    }
}
Bernardo Pacheco
  • 1,445
  • 1
  • 14
  • 23
  • I tried your code above and it worked the same as the previous code. Where are you caching the result? – Brooke Clonts Aug 01 '16 at 18:15
  • The code above works, but it depends where you're defining your service. It's a good idea to add this services to the app component providers, so it'll live while your app is running and all the routes will fetch the cached result. Check if you added the services in the app component provider. I'm caching the results in the products variable. – Bernardo Pacheco Aug 01 '16 at 18:28
  • Why negative? Please, provide a feedback instead of just -1. – Bernardo Pacheco Aug 02 '16 at 14:08
  • Sorry I didn't give it a negative but I did a plus one for you so that should counteract it. I think I've found the answer. I've got it working as a constructor rather than a provider. So I think your code would work with the bootstrapping and the constructor – Brooke Clonts Aug 02 '16 at 15:53
0

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.]

Brooke Clonts
  • 465
  • 1
  • 10
  • 20