0

In the constructor of an Angular service, I am creating an observable to which I need to subscribe directly (no inner subscription). How to do it? In particular, in the first block of code below I have the variable data.id.

this.current_trainee = this.db.collection('Users', ref => ref.where(
  'email', '==',  'EXAMPLE'  )).snapshotChanges().pipe(
  map(changes => {
    return changes.map(a => {
      const data = a.payload.doc.data() as Trainee;
      data.id = a.payload.doc.id;
      return data;
    });
  })
);

Now I want to subscribe to it directly to use it in doc(data.id)" below:

 this.trainees = this.db.collection('Users').doc( data.id ).collection('Trainings_taken').snapshotChanges().pipe(
  map(changes => {
    return changes.map(a => {
      const data = a.payload.doc.data() as Trainee;
      data.id = a.payload.doc.id;
      return data;
    });
  })
);

I tried to subscribe to it in the following way:

   this.current_trainee.subscribe(data => this.user_doc_id = data.id );

where user_doc_id is initiated as an empty string. But it does not work because user_doc_id stays an empty string. Any suggestion please? Thx in advance!

Annex
  • 25
  • 5
  • Possible duplicate of [Chaining RxJS Observables from http data in Angular2 with TypeScript](https://stackoverflow.com/questions/35268482/chaining-rxjs-observables-from-http-data-in-angular2-with-typescript) – dream88 Dec 17 '18 at 15:17
  • Think what you need is to wait for your first request to finish, this is why you see `user_doc_id ` is empty. The DB calls are asynchronous. Hence you need to chain these observables i.e. When you have the `data.id` from call1, use that to call the function2.. Please have a look at [this answer](https://stackoverflow.com/questions/35268482/chaining-rxjs-observables-from-http-data-in-angular2-with-typescript). And for [docs](https://www.learnrxjs.io/operators/combination/) for general combination opertaors – dream88 Dec 17 '18 at 15:20
  • @dream88 thanks! In that example, however, two functions must be called after each other, in my case there are two variables which must be defined consequently. Probably the solution is very similar but I do not really get it. Should I write a function which would comprise both variable definitions? – Annex Dec 17 '18 at 16:39
  • Maybe i misunderstand, when you say _consequently_ , you mean in parallel? I thought yours is one after the other too, as you need the response of one call (which gives data.id) to make the second call (to get the this.trainees). – dream88 Dec 17 '18 at 16:55
  • You understood it correctly, by consequently I mean after each other, chronologically. My problem is that I am using the operator "map" already and I want to call variables and not functions after each other. I really do not how how to tackle it. I have read about all kinds of operators: flatMap, switchMap and co. but somehow I do not see how to apply them for my problem. – Annex Dec 17 '18 at 16:59

2 Answers2

0

So based on the discussion, I have made a very simple example. The point here is you need to chain your 1st response (which gives you the data.id) and use this to call the second db function directly (instead of using 2 variables). The reason, as i mentioned before is, you make an asynchronous call to the DB to get your ID. This will arrive at some point later in time, and only then you can make the call using this ID. In your current code you are just calling the function this.db.collection('Users').doc( data.id )..} immediately, synchronously which wont work.

I did not use the flatMap as you donot have an inner observable (which i thought was the case)

import { of, Observable } from 'rxjs';
import { mergeMap,map } from 'rxjs/operators';

/**
 * Assume this is the response of the 
 * this.db.collection('Users', ref => ref.where(
 * 'email', '==',  'EXAMPLE'  )).snapshotChanges()
 * i.e an observable output 
 * and data_id is equivalent to data.id in your  
 */
const source = of({data_id: 0}, {data_id:1 },{data_id:2});

/**
 * Now if the data_id is the index to this array, in your case : you need the 
 * data.id to make a call to the `DB` to retrieve the trainees
 */
const db2 = ['a','b','c'];

//use map to get the ID you need and then directly call the db to retrieve trainees, in this case it will return you another observable
const example = source.pipe(map((e,i) =>  of(test(e.data_id))));

// subscribe to the observable and do your action
const subscribe = example.subscribe((val) => console.log(val));

function test(value) {
  return (db2[value]);
}

Stackblitz for you to play around.

dream88
  • 521
  • 2
  • 9
  • dream88, many thanks!!! This was very helpful and is exactly what I was looking for. I get what pipe and map are doing, it is right now just about accessing the data. I am doing the following: `const obs_id = this.current_trainee.pipe(map( val => val ));` `const doc_id = obs_id.subscribe(val => console.log(val) );` in the console I get plotted '[{…}]' an array with different properties and one of them is **id**. Do you know how can I access it? Since '`const obs_id = this.current_trainee.pipe(map( val => val.id ));` or similar stuff is not working. Thx! – Annex Dec 18 '18 at 17:22
0

So at the end of the day see below what works for me. It is not very elegant though because a part of the code repeats itself, but it works and I wanted to close this question. Thx @dream88!

this.current_trainee = this.db.collection('Users', ref => ref.where(
  'email', '==',  aux  )).snapshotChanges().pipe(
  map(changes => {
    return changes.map(a => {
      const data = a.payload.doc.data() as Trainee;
      data.id = a.payload.doc.id;
      return data;
    });
  })
  );

this.individual_trainings = this.db.collection('Users', ref => ref.where(
  'email', '==',  aux  )).snapshotChanges().pipe(
  map(changes => {
    return changes.map(a => {
      const data = a.payload.doc.data() as Trainee;
      data.id = a.payload.doc.id;
      return data;
    });
  }),
  switchMap((data) => {
    console.log(data);
    console.log(data[0].id);
    return this.aux2 = this.db.collection('Users').doc(data[0].id).collection('Trainings_taken').valueChanges();
  })
  );
Annex
  • 25
  • 5