1
votes

Hello Stackoverflow community,

I'm new to Angular and observables and wondering what is the best way to manage observables subscriptions. The database for the web app I am developing is Google's Firestore.

I need to retrieve the list of all the documents within a collection and listen for changes. At the moment, this is how I manage the subscription. I simply subscribe in ngOnInit and unsubscribe in ngOnDestroy within the component :

  export class AddDepartmentComponent implements OnInit, OnDestroy {

  FirestoreSubscription;
  businessUnitsListArray;

  constructor(private afs: AngularFirestore) { }

  @HostListener('window:beforeunload')
  ngOnDestroy(): void {
    this.FirestoreSubscription.unsubscribe();
  }

  ngOnInit() {
    this.FirestoreSubscription = this.afs.collection('BusinessUnits').valueChanges().subscribe(result => {
      this.businessUnitsListArray = result;

      // Do some manipulations with the data here
      // ...

    });

  }


}

I'm not a true fan of having this kind of subscription in all the components that need to access data from Firestore. I would like to create a Firestore service to manage all the calls to the DB. The service would look something like this (let's say it only contained this one function) :

export class FirestoreService {

  constructor(private afs: AngularFirestore) {}

  retrieveAllBusinessUnits() {
    return this.afs.collection('BusinessUnits').valueChanges();
  }
}

I would call it like this in the component :

  export class AddDepartmentComponent implements OnInit {

  businessUnitsListArray;

  constructor(private _firestoreService: FirestoreService) { }

  ngOnInit() {
    this.businessUnitsListArray = this._firestoreService.retrieveAllBusinessUnits();
  }


}

Is that a good way of doing it? It looks much cleaner and is much easier to maintain having everything in one service. Now, my understanding reading the angularfire docs is that this function returns an observable and using an async pipe in the template manages the subscription.

My questions are :

  1. I believe I have to subscribe to an observable in order to retrieve and manipulate its data. Wouldn't it be the same as doing what I am doing right now? Is there any benefits of having the FirestoreService if I have to subscribe to the observable in my component to then unsubscribe at a later time?

  2. Getting the data from Firestore this way (with the FirestoreService), how would I go about manipulating the data (differently for different components, hence not in the service) in a more complex way than what the HTML template can handle before using it in the component without simply subscribing to it to then unsubscribe in ngOnDestroy which would bring me back to square one like mentioned in #1? Using the method I use at the moment, I have a whole block (within the subscription parenthesis) to do any kind of manipulation I like, whether it be trigger another function when the returned data changes, or simply filter the returned data in a specific way that the Firestore querying functionality cannot handle easily.

  3. Do I have to unsubscribe somewhere even though I never explicitly subscribed (again assuming that I don't absolutely HAVE TO subscribe to the observable)?

Thanks to all for your support!

1

1 Answers

-3
votes

You can try the following approach

    // Service
        export interface dataModel{
           id: number;
           name: string;
        }

        export class FirestoreService {

          dataCollection: AngularFirestoreCollection<dataModel>;

          constructor(private afstore: AngularFirestore) {}

          getData() {
           this.dataCollection = this.afstore.collection('path');
           return this.dataCollection.valueChanges();
           // or use pipe operator and manipulate the data
         }

        }


  // Component

   dataList: dataModel[];

   constructor(private firestoreService: FirestoreService) {}

   ngOnInit() {
     this.firestoreService.getData().subscribe((res: dataModel[]) => {
       this.dataList = res;
    }, err => {
      // handle err 
   });
  }