0

I have an angular app where I am using a third party chat plugin ng-chat. To implement its chat adapter I have to override a method

getMessageHistory(userId: any): Observable<Message[]>

In this method I am calling my service method to get the data from my server

 this.userService.getChatHistory 

and subscribe to it , once the data is available I want to construct the array of messages and return from the overridden method getMessageHistory But for some reason the statements in getMessageHistory after the subscribe call is getting executed before the subscribe's callback are called. Below is my code

Service method

  public getChatHistory(loggedInUserId,selectedUserId){

         let data = {"requestingUserProfileId": loggedInUserId, "targetUserProfileId":selectedUserId};
        return this.http.post(this.apiEndPoint + '/xxx/', data, this.jwt())
            .map((response: Response) => response.json());


    }

In my adapter implementation

getMessageHistory(userId: any): Observable<Message[]> {
 let msgHistory: Array<Message> =[];
this.userService.getChatHistory(this._loggedInUserId,userId).subscribe(
                  messages => { 
             if(messages ==null || messages.length ==0){
                this.messages = [];
             }
             else{
             console.log("messages retrieved are "+messages);
             this.messages = messages;



            for (var msg of  this.messages) {

                 let tempMsg :Message = new Message();
                 console.log("from id is ",msg.fromId);
                  tempMsg.fromId= msg.fromId;
                  tempMsg.toId= msg.toId;
                  tempMsg.message= msg.messageText;
                  msgHistory.push(tempMsg);

            }

             }});
  console.log("msgHistory array ",msgHistory);  //==> This is getting //executed before the  messages => { in the subscribe above
            return Observable.of(msgHistory);


    }

In My output I can see msgHistory array [] being printed before

messages retrieved are [object Object],[object Object]

So my msgHistory array being returned is always empty. Is there a way that empty msgHistory is not returned, i.e. code should wait for subscribe call to finish before proceeding with the return statement?

I have also tried putting the logic

this.userService.getChatHistory(this._loggedInUserId,userId).subscribe

in a separate function and returning it, even then same issue.

TruckDriver
  • 1,383
  • 13
  • 28
  • There's a bit of a design problem here... what is `this.messages`? Where is it used? How does that differ to `getMessageHistory()`? @hemu's answer makes sense, but it has code smells that make it hard to understand the stream, but I don't know how to improve that without having more context on what your use case is. – olivarra1 Dec 11 '17 at 11:42

2 Answers2

1

You need to understand that subscribe is an asynchronous operation; which means the observable you have subscribed to, should emit values asynchronously. Only one solution I can think of is to convert the Observable sequence into a Promise and put the console.log statement inside the .then() function. You can do it with the toPromise function. For more details, check http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-toPromise.

Manoj Patra
  • 51
  • 1
  • 9
0

Not sure, but could you try following code with first and flatMap operator of rxjs :

getMessageHistory(userId: any): Observable<Message[]> {
 let msgHistory: Array<Message> =[];
 return this.userService.getChatHistory(this._loggedInUserId,userId)
            .first()
            .map(messages => { 
                if(messages ==null || messages.length ==0){
                    this.messages = [];
                } else {
                    console.log("messages retrieved are "+messages);
                    this.messages = messages;
                    for (var msg of  this.messages) {

                        let tempMsg :Message = new Message();
                        console.log("from id is ",msg.fromId);
                        tempMsg.fromId= msg.fromId;
                        tempMsg.toId= msg.toId;
                        tempMsg.message= msg.messageText;
                        msgHistory.push(tempMsg);

                    }

                }
                return Observable.of(msgHistory);
            });

}
hemu
  • 3,199
  • 3
  • 44
  • 66
  • Why map+flatmap of Observable.of(...)? You can let map own `msgHistory` and return that, so you won't need to `flatmap` – olivarra1 Dec 11 '17 at 11:39
  • @olivarra1 - Actually that's correct. We can also return Observable.of(..) from map once response processed. Correcting. – hemu Dec 11 '17 at 11:44
  • Still this needs to be tested with actual APIs by @TruckDriver. – hemu Dec 11 '17 at 11:45