Angular 6 and Firestore: best way to loop through and update each Document in AFS Collection?

Viewed 2286

This is an Angular 6 and Firestore project. I am trying to iterate through and update each document in my articlesCollection. A click event triggers the method that does the loop. The problem is, sometimes the method loops through the collection only once. Other times it loops infinitely. Is there a problem with my method? Is there a better way to loop through and update each doc in a collection?

Edit: When I hard reload localhost:4200, the first time I trigger the method it loops through once. If I trigger the method a second time, it loops infinitely.

featureArticle(article) {
    const articleArray = this.articlesCollection.snapshotChanges();

    articleArray.subscribe( payload => {
      payload.forEach( item => {
        const ID = article.id;
        const articleID = item.payload.doc.data().id;
        const articleTITLE = item.payload.doc.data().title;
        const articleToUpdate = this.articlesCollection.doc(`${articleID}`);
        if (ID === articleID) {
          articleToUpdate.update({
            isFeature: true
          });
        } else {
          articleToUpdate.update({
            isFeature: false
          });
        }
        alert(`${articleTITLE} updated`); <-- LOOPS INFINITELY
      });
  });
}

The template:

<ng-container *ngFor="let article of articles | async">          
  <div (click)="featureArticle(article)">
     {{ article.title }} 
  </div>   
</ng-container>
2 Answers

You are listening on changes and acting on changes. And acting on changes will trigger the listener since you are changing something.

You will ofcourse remain in an endless self-imposed loop.

A soulution would be: listening on changes on some trigger doc A. This could be a simple doc for just this purpose: listening when there has to be changes done on doc B. Then this onchanges on A listener is triggering the Method you wanted to call in the first place to update any doc B. So if doc B is updated, there won't be a infinite loop, since doc A isnt getting any change triggers if doc B is updated.

Okay, so my fix here was to create an empty IDs = [] variable in the component. In the constructor, I looped through my articlesCollection with snapshotChanges().foreach() and pushed all of my document (article) IDs to the array. My featureArticle(article) method could then loop through the IDs array and compare article.id to each ID. If there was a match, I did X. If there wasn't a match, I did Y.

The point of this was to write a method that would set any given Article's isFeature? property to true while simultaneously setting all other Articles isFeature? properties to false.

export class AdminArticleListComponent {
  articlesCollection: AngularFirestoreCollection<Article>;
  IDs: Array<any> = [];

  constructor( stuff here ) {
    this.articlesCollection = this.afs.collection('articles'); 
    this.articlesCollection.snapshotChanges().forEach( a => {
      a.forEach( item => {
        const id = item.payload.doc.data().id;
        this.IDs.push(id);
      });
    });

  }

  featureArticle(article) {
    for (let id of this.IDs) {
      const document = this.articlesCollection.doc(`${id}`);
      const articleID = article.id;
      if (id === articleID) {
        document.update({
          isFeature: true
        });
      } else if (id !== articleID) {
        document.update({
          isFeature: false
        });
      } else {
        console.log('Error updating feature Article');
      }
    }
  }

}

And the template:

<ng-container *ngFor="let article of articles | async">
  <tr>
      ... other cells ..
      <td class="featureCell" (click)="featureArticle(article)">
          <ng-container *ngIf="article.isFeature">
              <span class="greenCheckmark">&#9989;</span>
          </ng-container>
          <ng-container *ngIf="!article.isFeature">
              <span >&#9989;</span>
          </ng-container>
      </td>
  </tr>
</ng-container>
Related