1

Can't seem to wrap my head around chaining / wrapping observables. I am using Angular2 rc1 with RxJs in Typescript in Visual Studio 2015.

I have a servicemethod 'saveProduct' in the ProductService class:

public saveProduct(product: Product): Observable<string> {
    let options = new RequestOptions({ headers: new Headers({ 'Content-Type': 'application/json' }) });
    return this.http.post(this.config.apiUrl + '/product', JSON.stringify(product), options).map(this.extractData).catch(this.handleError);
}

I consume it in an angular2 component:

public save() {
    this.productService.saveProduct(this.product).subscribe(
        result => this.productSaveExecuted(result),
        error => this.handleError(error)
    );
}

The component is wrapped in a modal dialog wiIf I would close the dialog after calling the component's save method, the dialog would be closed before the save action is finished. So, figure I want the component's save function to also return an Observable, because the component is wrapped in a modal div witch I want to close after a successful save. How do I accomplish this?

Mcanic
  • 1,304
  • 16
  • 22

2 Answers2

1

Something like this:

public save(): Rx.Observable<{ success: boolean }> {
    return this.productService
        .saveProduct(this.product)
        .select(result => {
            this.productSaveExecuted(result);
            return { success: true };
        })
        .catch(error => {
             this.handleError(error);
             return Rx.Observable.return({ success: false });
        });
}

The save method will return an observable that on subscription will try to save the product.

Subscribing to the save method:

save()
    .subscribe(result => {
        if(result.sucess)
            // everything went well
        else 
            // something went wrong
    })

I think this is what you want.. you are not entirely clear in your question.

Nypan
  • 6,980
  • 3
  • 21
  • 28
  • Thank you for your reply, I think your answer is exactly what I am aiming for. However, VS2015 gives the error: Property 'select' does not exist on type 'Observable'. It seems Angular2 only uses a subset of rxjs? I do see select.js in the directory node_modules\rx\src\core\linq\observable\ How do I import this in my Angular2 class? – Mcanic May 27 '16 at 08:51
  • 1
    Ok I am not super fluent in Angular2, but if the `Observable` returned by `saveProduct` is an `Rx.Observable` select should be present on it. Try changing the output type to `Rx.Observable`, if that does not work you might need to include more of Rxjs in your solution (rx.all.js and rx.all.d.ts includes everything). Take a look at https://github.com/Reactive-Extensions/RxJS for mor information on rxjs in general. – Nypan May 27 '16 at 11:43
  • Well, after pondering on Observables, it's not so easy to wrap an observable in another Observable, thus passing it to a parent object. The select statement that you came up with does not exist in the RxJS included in Angular 2 rc1. You can chain Observables by using the ***Observable.flatmap*** operator and return the resulting Observable, but you can also just return the 'then' promise from the Observable's toPromise method. Will get back on this soon. – Mcanic May 31 '16 at 21:22
  • 1
    Ok `select` is the same thing as `map` and `flatMap` is the same thing as `selectMany`, they are just aliases for each other. So if `select` does not exist try `map`. And if that does not work you can import the operators you need, take a look at http://stackoverflow.com/questions/34113750/angular-2s-http-service-not-exposing-map-and-other-rxjs-functions/34113985, not sure how up to date that is but if you look in to it someone will have solved how to get access to the rxjs operators in angular. It is an essential part of reactive programming. – Nypan Jun 01 '16 at 08:00
0

Observables can be 'wrapped' in another observable like this:

public save(): Observable<{}> {
    console.log("productdetails.component save was called");

    return Observable.create(observer => {
        this.productService.saveProduct(this.product).subscribe(
            result => this.productSaveExecuted(result),
            error => this.handleError(error)
        );
        observer.next(),
        function (err) {
            console.log('Error: ' + err);
        },
        //call complete if you want to close this stream (like a promise)
        observer.complete();
    });

}

An other solution would be to subscribe to the resulting Observable from the productService and return the promise from the .toPromise() method.

Thank you Nypan for supporting my learning process:)

Mcanic
  • 1,304
  • 16
  • 22