EDJ
EDJ

Reputation: 1023

Angular - combining Observables with promise

Currently, I am calling the database to extract all the conversations of a specific user. After that, I am looping through those conversations and I extract metadata such as message text, message sender and etc from each conversation. I create an object for each with its' metadata which then are all stored in an Array - in my case this.recentChats.

this.af.getObservable(`userChats/${this.userID}/`).pipe(
  // we map the array of conversations to an array of the (before inner) observables

  map(recentConversations =>
    recentConversations.map(conversation =>
      this.af.getObservableSortByVar(`allChats/${conversation.conversationID}/`, 'lastUpdate'))),

  // combine the observables. will emit a new value of ALL conversation data when one of the conversations changes

  switchMap(recentConversations => combineLatest(recentConversations)),
  // map each conversation to the conversation object 
  map(conversations =>
    conversations.map(conversationData => {
      let userKey, lastMessage, lastMessageText, lastMessageSender, lastMessageDate;

      for (let i = 0; conversationData.length > i; i++) {
        switch (conversationData[i].key) {
          case 'messages': {
            lastMessage = (conversationData[i][Object.keys(conversationData[i])[Object.keys(conversationData[i]).length - 1]]);
            lastMessageText = lastMessage.message;
            lastMessageSender = lastMessage.sender;
            lastMessageDate = lastMessage.date;
          }
          case 'users': {
            userKey = conversationData[i].userKey;
          }
        }
      }
      return this.createConversationObject('username', userKey, lastMessageSender, lastMessageText, lastMessageDate);
    }))
).subscribe(recentChats => this.recentChats = recentChats);

However, on the line where return this.createConversationObject('username', ...) is called, I have been struggling to assign a user's actual username, fetched from the database.

Basically, I am using the userKey local variable, which is obtained from the switch-case code above, in order to attempt to do the following:

let tempUsername;
this.af.getUserName(userKey).then((username) => {
   tempUsername = username;
}

And then pass the tempUsername in the return statement. However, I have been struggling with making the result would to be awaited - in order for the username to be fetched, before the return is called.

Does anyone have any suggestions how can I properly achieve the desired result?

Upvotes: 3

Views: 464

Answers (1)

akhouri
akhouri

Reputation: 3165

Edit (after understanding the issue more closely)

Here's the code example for the proposed solution: Code example

Here are the modification that needs to be done to your code:

  • In the map(conversations => block, return the modified object with userkey appended and not the results of this.createConversationObject()

  • introduce switchMap after this and convert the Promise to Observable using rxjs -> from operator, also convert the results of the updated object returned from the previous map operation into a new Observable using rxjs -> of operators

  • now return the result of CombineLatest(OriginalObservable, fromPromiseObservable)

  • lastly you further need to map the results of these two Observable returns into one of use it directly in your call to this.createConversationObject()

  • and finally you can subscribe to the result to get the actual result as a final step

Hope this clarifies!

Original response

Here's my suggestion:

  • You could convert this.af.getUserName(userKey) promise to an observable first by using fromPromise provided by rxjs.
  • and then use this converted observable for username in your existing CombineLatest in the SwitchMap block
  • and finally use the result which would now be conversions and username in your map block to fetch the username for your return statement

Upvotes: 1

Related Questions