Search code examples
angularsubscribe

Got undefined properties from subscribe not finished


I'm using Angular and AngularFire to get data from my Firestore Database.

I have two services 'itemsService', 'detailsItemService' which collect and return data from my Firestore collection 'items' and 'detailsItem';

// it's the same for detailsItemService
[...]
export class itemsService {

  private items: Observable<Item[]>;

  constructor(
    private db: AngularFirestore,
    private userService: userService
  ) {
      this.items = db.collection<Item>(
        'items',
        ref => ref.where('userID', '==', this.userService.userID)
      )
      .snapshotChanges()
      .pipe(
        map(array => {
          return array.map(item => {
            return {
              uid: item.payload.doc.id,
              ...item.payload.doc.data()
            } as Item;
          });
        });
      );
  }

  getItems() {
    return this.applications;
  }
}

Then in my controller 'itemsCtrl', I make a subscription to get my 'Items' with uid and I proceed to a forEach to make a subscription 'detailsItem' of each Item with its uid.

[...]
export itemsCtrl implements OnInit {

  public items = [];

  constructor(
    private itemsService: itemsService
  ) {
    const items = [];

    this.itemsService.getItems().subscribe(items => {
      items.forEach((item, key) => {
        items[key] = item;

        this.detailsItemService.getDetails(item.uid).subscribe(details => {
          items[key].details = details;
        });
      }
      this.items = items;
    });
  }

  [...]
}

But in my view, I got undefined properties when the page starts to load.

<div *ngFor="let item of items">
  <input [(ngModel)]="item.details.name">
</div>

Then I get this error :

ERROR TypeError: Cannot read property 'name' of undefined where undefined id 'details'

Thanks for helping :')


Solution

  • Use this.items directly (getDetails is async function and this.items = items will be evaluated before the subscription is completed):

    this.itemsService.getItems().subscribe(items => {
          items.forEach((item, key) => {
            this.items[key] = item;
    
            this.detailsItemService.getDetails(item.uid).subscribe(details => {
              this.items[key].details = details;
            });
          }
     });
    

    And check if details exists:

    <input *ngIf="item.details" [(ngModel)]="item.details.name">