2

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.

4 Answers 4

3

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);
  }
Sign up to request clarification or add additional context in comments.

1 Comment

I like this answer because I dont need to refetch the observable :) Thank you
3

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);
        }
    }
  }

1 Comment

NOT a good approach. This would make a second subscriber in memory. For every change 2 functions would be called.
1

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);

Comments

0

You'll have to re-fetch the data to iterate, because contactsList may have been consumed already. So another call to this.fb.getContactsByGroup(this.group.id) is necessary.

public sendSMS(): void {
  // use .first() to just process once
  this.fb.getContactsByGroup(this.group.id).first().subscribe(contacts => {
    contacts.forEach(contact => {
      console.log(contact.number);
    });
    // <>
  })
  
  // notice the code above is asynchronous, so if the line below
  // depends on it, place it on the line marked with "// <>" above
  //this.deviceProvider.sendSMS(this.contactsList, this.message);
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.