Geoffrey D
Geoffrey D

Reputation: 588

RxJS and typescript : Observable.flatMap do not behave correctly

In typescript, it seems that the RxJS Observable.flatMap is not behaving correctly with Observable created with Observable.fromPromise(promise).

I suspect that it does not trigger the change detection.

I have these 2 functions (in _httpClient class) :

ObservableFromPremise() : Observable<boolean>{
        var promise =  this._storage.getJson('authToken').then((token) => {
            return true;
        });
        return Observable.fromPromise(promise)
}

BasicObservable() : Observable<boolean>{
     return Observable.create(observer => {
         observer.next(true);
         observer.complete();
     });
}

When I do this :

  public get = (url: string) : Observable<Response>  => {

       return this.ObservableFromPremise()
        .flatMap((x) => {
                return this.http.get(url, {headers:this._headers})
                    .map( (responseData) => {
                        return responseData.json();
                    });
           });
    }

My view in not updated, and I need to click somewhere (like a button) to have the retrieved data displayed.

But when I use BasicObservable() instead of ObservableFromPremise(), my view is updated.

Here is how I deal with the function get (in UserConnector class) :

public makeRequest = (id: number): Observable<User> => {
    return this._httpClient.get('http://jsonplaceholder.typicode.com/posts/1')
        .map((item:any) => {
            return new User({
                id: item.id,
                userId: item.userId,
                title: item.title,
                body: item.body
            });

        });
}

and in my page :

public myItems: User;

constructor(private userConnector: UserConnector) {

}

ngOnInit() {
        this.getAllItems();
}

private getAllItems(): void {
    this.userConnector
        .makeRequest(this.selectedItem.id)
        .subscribe((data:User) => {
            console.log(data);
            this.myItems = data;
        },
            error => console.log(error),
            () => {console.log('Get Item completed'); });
}

And here is the template :

     <div class="thumbnail" *ngIf="myItems">
         <div class="caption">
             <h3>{{myItems.title}}</h3>
             <p>{{myItems.body}}</p>
             <p>{{myItems.id}}</p>
             <p>{{myItems.userId}}</p>
         </div>
     </div>

In Both cases, I have the data retrieved logged in console, as well as "Get Item completed", but with ObservableFromPremise(), the data are not updated on the screen (until I click on a button).

My config : angular2 (2.0.0-rc.1), RxJS (5.0.0-beta.6), zone.js (0.6.12), e6-shim (0.35.00)

What can I do about this? Is it a bug in my code? in zone.js? in RxJS?

Thx for your help

Edit 1 : As suggested by @Richard-Silveira I will use NgZone as a temporary workaround :

.subscribe((data:User) => {
   this._ngZone.run(() => {
      this.myItems = data;
   });
}

I hope that someone will share a real solution =)

Upvotes: 1

Views: 1255

Answers (2)

cp79shark
cp79shark

Reputation: 1

Doesn't let me comment yet, guess I should build rep. Answering for Angular 4, in case you're still on 2 for whatever reason.

This is not a RxJS problem from the sounds of it (as your console logs show). You don't post the relevant bits from your component (like the decorator), but I'm making the assumption that you're using the OnPush change detection strategy?? If so, then you'll need to mark myItems with the @Input() decorator.

@Input() public myItems: User;

Optionally, if you want to go the brute force route, you can inject a ChangeDetectorRef and call markForCheck() on it.

constructor(private changeRef: ChangeDetectorRef) {}
...
// in your subscribe()
this.myItems = data;
this.changeRef.markForCheck(); // or call detectChanges() to immediately do so 

Upvotes: 0

Richard Lee
Richard Lee

Reputation: 2245

Should you use NgZone to update the UI as follow:

private update() {
    this._zone.run(() => {
        console.log('auth updated!');
    });
}

...and after the return in your subscribe call this update function:

.subscribe((data:User) => {
            this.update();
            this.myItems = data;
        },

Upvotes: 2

Related Questions