How to loop through (forEach) on an Observable which has been subscribed by the template

14,779

Solution 1

You will need to manually get the result of Observable in a variable in you component.

constructor(
     public fb: FirebaseProvider, 
) {
     this.group = this.navParams.get('group');
     this.fb.getContactsByGroup(this.group.id).subscribe(list => 
        this.contactsList = list);    
  }

and change your template to render only when the contactList is available. And iterate over the the list.

<ng-container *ngIf="contactsList">
    <single-contact-list-item 
    [contactId]="contact.id" 
    (goToModalEvent)="onGoToModal($event)"
    *ngFor="let contact of contactsList">
    </single-contact-list-item>
<ng-container>

By doing this your function sendSms will work as it is and will get the list of conatcts.

public sendSMS(): void {
    this.contactsList.forEach(contact => {
      console.log(contact.number); // WORKING!!!!
    })
    //this.deviceProvider.sendSMS(this.contactsList, this.message);
  }

Solution 2

You need to subscribe to an Observable first to access data stream, then do loop. Try

public sendSMS(): void {
    this.contactsList.subscribe(lists=>{
        lists.forEach(contact=>{
            console.log(contact.number);
        }
    }
  }

Solution 3

You can reuse your observable containing your array and perform some operations to your stream in order to pull out the interesting data. If it is just for side effects related to your data, you can use a tap operator and do your forEach inside.

 public sendSMS(): void {
    this.contactsList.pipe(
      tap(contactArray => {
         contactArray.forEach(contact => {
           // Do side effect operations with your array
         });
      }),
      // execute only once and complete the observable
      take(1)
    ).subscribe();
 }

   this.deviceProvider.sendSMS(this.contactsList, this.message);
Share:
14,779

Related videos on Youtube

rhysclay
Author by

rhysclay

Updated on June 04, 2022

Comments

  • rhysclay
    rhysclay almost 2 years

    I have an AFS observable which I can show in my frontend like this:

    constructor(
        public fb: FirebaseProvider, 
    ) {
        this.group = this.navParams.get('group');
        this.contactsList = this.fb.getContactsByGroup(this.group.id);
    }
    

    My fb service:

    getContactsByGroup(groupId: string):Observable<Contact[]> {
        return this.afs.collection<Contact>(`groups/${groupId}/contacts`).valueChanges();
    }
    

    Then my template:

    <single-contact-list-item 
        [contactId]="contact.id" 
        (goToModalEvent)="onGoToModal($event)"
        *ngFor="let contact of contactsList | async">
    </single-contact-list-item>
    

    Now I have a textarea which when submitted triggers an event. What I want to do is loop through each item in the Observable (contactsList) and get a property, for example:

    public sendSMS(): void {
        this.contactsList.forEach(contact => {
            console.log(contact.number); // nothing!
        })
        //this.deviceProvider.sendSMS(this.contactsList, this.message);
    }
    

    I feel like the issue is that by using the async pipe in the template the Observable is already subscribed. Any help appreciated.

  • Naman Kheterpal
    Naman Kheterpal about 6 years
    NOT a good approach. This would make a second subscriber in memory. For every change 2 functions would be called.
  • rhysclay
    rhysclay about 6 years
    I like this answer because I dont need to refetch the observable :) Thank you