0

Just to clarify. This angular app is populating the data to the view but when I navigate from one component to the other the ngOnInit lifecycle hook does not seem to get invoked thus meaning that the list on the view remains empty. When i hit f5 (refresh) the data on whichever page i am gets populated meaning that the service is working alright. Could it be that the life cycle hook is the issue?

So given that i have the following collection in the file product.service.ts

 export class ProductsService {

  private productsCollection:AngularFirestoreCollection<Product>;
  private products:Observable<Product[]>;

  constructor(private afs:AngularFirestore) { 
    //Code was removed from here as it is beyond the scope of the problem
    //this.products is getting populated here successfully 
  }

 getProducts(){
   return this.products;
 }
}

And i subscribe to the method from two separate components as follows (using the same exact code below)

constructor(private productService:ProductsService) { }

      ngOnInit() {
        this.productService.getProducts().subscribe(
          products => {
              this.products = products;
          });

So assuming that componentA and componentB both have this code, the lists should be populated on both components when they are loaded. When i key in the URL to the component the list is populated but when i use routerlinks and the data is not populated and its like as if ngOnit is not triggered when the new route is invoked. Both views show the lists but they don't get populated when i use routerlink as opposed to writing the address or refreshing the page. The service is shared in the app.module.ts as a provider. Some help is appreciated.

Metrophobe
  • 83
  • 1
  • 9
  • It doesn't look like you're ever setting the data in the service so it will always be null? – Jack_b_321 Mar 27 '19 at 16:39
  • the data is being set and there is no problem whatsoever i will add a comment in there. All is good on the views as i iterated in the comment. – Metrophobe Mar 27 '19 at 17:18

1 Answers1

1

Use a behaviour subject rather than an observable. A behaviour subject is a hot observable that allows any subscribers to pull existing data that has already been pushed to it. One difference with behaviour subject is that you push data using the .next method. BehaviorSubjects also need to be passed an initial value when being initialised in this case I have passed null as I assume your constructor does something in order to fetch the data needed. Code example below:

 export class ProductsService {

     private productsCollection:AngularFirestoreCollection<Product>;
     private products:BehaviorSubject<Product[]> = new BehaviorSubject(null);

     constructor(private afs:AngularFirestore) { 
       //code here to set products
       this.products.next('data to set products here')
     }

     getProducts(){
        return this.products.asObservable();
     }
 }
Jack_b_321
  • 848
  • 8
  • 22
  • 1
    `ReplaySubject(1)` is an alternative if OP does not have an initial value to pass to `BehaviorSubject` – UncleDave Mar 27 '19 at 17:57
  • will explore both possibilities but it is still unclear why the collection does not live during the page change i will try it out and get back and update. – Metrophobe Mar 27 '19 at 18:05
  • 1
    An observable is not a collection, when a value is emitted it is passed to any subscribers, however any new subscribers (your component being created) will not get previously emitted values. A `BehaviorSubject`, as noted above, DOES remember the previous value and deliver it to new subscribers. – UncleDave Mar 27 '19 at 18:07
  • The same can be achieved by returning an observable from a regular set of data. You can store the products as Product[] and then in your getProducts method: `return of(this.products)` this however will not trigger any subscribers if you wish to update products at any point in any other place. BehaviorSubject is probably the best way to go for this issue. – Jack_b_321 Mar 27 '19 at 18:11
  • @UncleDave thanks for the pointers but am aware of 'hot' observables. I was of the impression that if you use a pipe(map(doing mapping here), share()) I was thinking that i was turning a regular observable into a 'hot' Observable. – Metrophobe Mar 27 '19 at 19:40
  • I believe `share` will prevent the pipeline running for every new subscriber, however it won't replay old values to new subscribers, you may be thinking of [shareReplay](https://www.learnrxjs.io/operators/multicasting/sharereplay.html). – UncleDave Mar 27 '19 at 19:53
  • ok reporting back... for some odd reason the share() method was not working (and turning an Observable into a 'hot' Observable. This however leaves me with another issue. Since I am using AngularFire2 (Firestore) to retrieve the data. snapshotChanges() returns an Observable and not a BehaviourSubject.... But i worked around it and hence resolved the issue. – Metrophobe Mar 27 '19 at 20:42