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!

1

3 Answers 3

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));
Sign up to request clarification or add additional context in comments.

Comments

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');
    }
}

4 Comments

I tried your code above and it worked the same as the previous code. Where are you caching the result?
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.
Why negative? Please, provide a feedback instead of just -1.
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
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.]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.