0

Let's have a biz model defined like this

export inteface Item {
    id: string;
    name: string;
}

export interface Container {
    id: string;
    items: Item[];
}

In Cloud Firestore we have collection /containers and each container contains collection /items with documents. Each document has field name. So the structure inside Cloud Firestore copies our biz model.

Now I'd like to retrieve specific container with its collection of items.

getContainer(idIn: string) {
  const document: AngularFirestoreDocument<Container> = this.afs.doc('containers/' + idIn);
  const document$: Observable<Container> = document.snapshotChanges().map(a => {
      const data = a.payload.data() as Container;
      const id = a.payload.id;
      return this.getItems(idIn).map( item => {
        return {id, ...data, items: item};
      });
    });
  return document$;
}

getItems(id: string) {
  const collection: AngularFirestoreCollection<Item> = this.afs.collection('containers/' + id + '/items');

  return collection.snapshotChanges().map(actions => {
    return actions.map(a => {
      const data = a.payload.doc.data() as Item;
      const id = a.payload.doc.id;
      return {id, ...data};
    });
  });
}

But i keep getting this error about missing id on returning Observable. Compiling with typescript 2.6.2.

error TS2322: Type 'Observable<Observable<{ items: { 'id': string;  'item': string; }[]; 'id': string...' is not assignable to type  'Observable<Container>'.
 Type 'Observable<{ items: { 'id': string; 'item': string; }[]; 'id': string...' is not assignable to type 'Container'.
 Property ''id'' is missing in type 'Observable<{ items: { 'id': string; 'item': string; }[]; 'id': string...'.

Could anyone point out what i am doing wrong?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Rob
  • 708
  • 8
  • 27

2 Answers2

1

It looks to me your return data is not same as the Interface modal. You only specified id and items fields in the interface, so it will be

  const data = a.payload.data() as Container;
  const id = a.payload.id;
  let items=[];
  this.getItems(idIn).valueChanges().subscribe(res=>{
      res.forEach(item=>{
        items.push(item);
      })
    });
  return {id:id, items: items};
Hareesh
  • 6,770
  • 4
  • 33
  • 60
  • At first glance i don't think this is also correct. But i was wondering, if reference `data` from mehtod `getContainer()` could overshadow the the reference with the same name `data` in the method `getItems()`. I am new to the javascript/typescript. Could it be scope issue?(i can't confirm it now) – Rob Jan 29 '18 at 10:25
  • try to change the name `const data` in `getItems()` and confirm. – Hareesh Jan 29 '18 at 10:31
  • i am not sure about the scope if you written inside another function – Hareesh Jan 29 '18 at 10:35
  • I would prezoom that calling method in typescript would also create the stack frame and move stack pointer but like i said i don't know typescript well. So it's possible it's all in 1 scope and 1st `data` is overshadowed. – Rob Jan 29 '18 at 10:44
  • One more thing i noticed you need to pass array of items. – Hareesh Jan 29 '18 at 10:51
0

I am reading nested collections in Firestore to populate in my case an Expansion widget. See my solution in https://stackoverflow.com/a/51057195/5013735

The trick is in to create a structure like:

  List<Widget> _getChildren() {
    List<Widget> children = [];
    documents.forEach((doc) {
      children.add(
        ProjectsExpansionTile(
          name: doc['name'],
          projectKey: doc.documentID,
          firestore: firestore,
        ),
      );
    });
    return children;
  }
Jobel
  • 633
  • 6
  • 13