AngularFirestore database, HTML table not updating when data changed using "onSnapshot"

Viewed 380

I'm using the onSnapshot to get realtime updates in the database.

I get the data for the table using this code:

Service.ts

export class ExtractorService {

  constructor(private fstore: AngularFirestore) { }

  sampleFunction() {
    return new Promise(resolve => {
      this.fstore.firestore.collection('banned').doc('words')
      .onSnapshot(function(doc) {
        //console.log("from service (WORDS): ", doc.data().cars)
        resolve(doc.data().cars);
      }); 
    });
  } 

To get the data on the service I use this code for the Component.ts

export class sampleComponent implements OnInit {

banWords = [];

  constructor(private extractorService: ExtractorService) {}

  ngOnInit() {
    this.loadBannedWords()
  }

 loadBannedWords(){
      this.extractorService.sampleFunction().then(data => {
      this.banWords = (data as unknown as string[]);
      console.log("words: ", this.banWords )
    })

  }

Then I load the table using this HTML:

<tr *ngFor ="let item of banWords ; index as i ">
      <td>{{ i+1 }}</td>
      <td>{{ item }}</td>

Then my database is look like this:

enter image description here

The issue here is when I add,update or delete data on the database, the table was not updated or reloaded automatically. I need to refresh the page in order for the table to be updated.

How can I make the table update when the database was updated?

1 Answers

You grab the data from the service only once, since you are returning a promise. Promise only return a value once and then they complete. What you need is an Observable.

You can define a public Observable in your service.

Something like:

export class ExtractorService {

  public myObservable$: Observable<Array<string>>`;

  constructor(private fstore: AngularFirestore) {
    this.myObservable$ = this.fstore.firestore.collection('banned').doc('words')
        .onSnapshot.pipe(map(doc -> doc.data().cars));
  }
}

This way your Observable will manipulate the snapshot Observable by only returning the cars to all its subscribers.

After you have defined your Observable, you can subscribe in every component, where you need the data. E.g

export class SomeComponent {

  private onDestroy$ = new Subject();

  constructor(private extractorService: ExtractorService) { }

  public ngOnInit() {
    this.extractorService.myObservable$
        .pipe(takeUntil(this.onDestroy$))
        .subscribe(data => {
      this.banWords = data;
      console.log("words: ", this.banWords )
    });
  }

  public ngOnDestroy() {
    // Unsubscribe all subscriptions of this component.
    this.onDestroy$.next();
  }
}

The benefit of using Observables is, that you can use them directly in you templates as well. E.g if your service would be public in your component, you could use the async pipe in your template to have access on the current values of the observable:

<div *ngFor="let car of extractorService.myObservable$ | async">
  {{ car }}
</div>
Related