How to return Observable inside a subscription

20,809

With your actual code in BackendService.ts:

return new Observable(obs=> {
    let obs1 = this.db.getActionUrl(key);
    obs1.subscribe(value => {
        let url = BackendService.BaseUrl + value;

        console.log("key: ", key);
        console.log("url: ", url);
        var h = BackendService.getHeaders();
        obs.next(this.http.post(
            url,
            JSON.stringify(requestObj),
            { headers: h }
        ).map((res: Response) => res.json()));
         obs.complete();
        ...
    });
 });

you emitted the http observable

this.http.post(
                    url,
                    JSON.stringify(requestObj),
                    { headers: h }
                ).map((res: Response) => res.json())

that's why you got: {"_isScalar":false,"source":{"_isScalar":false},"operator":{}} when subscribing on it, it's an observable.

The simplest solution with your code, you can emit the data after subscribing the second observable, something like:

return new Observable(obs=> {
    let obs1 = this.db.getActionUrl(key);
    obs1.subscribe(value => {
        let url = BackendService.BaseUrl + value;
        console.log("key: ", key);
        console.log("url: ", url);
        var h = BackendService.getHeaders();
        this.http.post(
            url,
            JSON.stringify(requestObj),
            { headers: h }
        ).map((res: Response) => res.json())
         .subscribe(data => obs.next(data));
    });
});

But the better solution is by using switchMap operator: (or any other xxxxMap operator)

import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/map';
...
sendPost(key: string, requestObj: Object):Observable<any>{
    return this.db.getActionUrl(key)
            .map( value => BackendService.BaseUrl + value)
            .switchMap(url => this.http.post(
                url,
                JSON.stringify(requestObj),
                { headers: h }
            )
            .map((res: Response) => res.json()))
}
Share:
20,809
AsyncTask
Author by

AsyncTask

software engineer

Updated on March 28, 2020

Comments

  • AsyncTask
    AsyncTask about 4 years

    I want to call http request inside a observable which makes select operation from database. I made two services, DbService and BackendService.

    BackendService makes http post requests and returns response data. In my design BackendService should subscribe to DbService for getting url, after that make http post request then return response data.

    BackendService can take url from DbService and try to make http request but couldn't. response data is (Json format)

    {"_isScalar":false,"source":{"_isScalar":false},"operator":{}}
    

    I don't understand what happening here. My services and AppComponent file is below.

    There is BackendService

    import { Injectable } from "@angular/core";
    import { getString, setString } from "application-settings";
    import { Headers, Http, Response, RequestOptions } from "@angular/http";
    import { Observable } from "rxjs/Observable";
    import 'rxjs/add/observable/of';
    import "rxjs/add/operator/do";
    import "rxjs/add/operator/map";
    import "rxjs/add/observable/throw";
    import "rxjs/add/operator/catch";
    import { DbService } from "./db.service";
    
    @Injectable()
    export class BackendService {
        static BaseUrl= "http://blabla.com"
    
        constructor(public http: Http, private db: DbService) {
        }
    
            sendPost(key: string, requestObj: Object):Observable<any>{
            console.log("sendPost: ");
            return new Observable(obs=> {
                let obs1 = this.db.getActionUrl(key);
                obs1.subscribe(value => {
                    let url = BackendService.BaseUrl + value;
    
                    console.log("key: ", key);
                    console.log("url: ", url);
                    var h = BackendService.getHeaders();
                    obs.next(this.http.post(
                        url,
                        JSON.stringify(requestObj),
                        { headers: h }
                    ).map((res: Response) => res.json()));
                    // .catch((error: any) => Observable.throw(error.json())));
                    obs.complete();
                }
                , error => {
                    console.error("send post error: "+ error);
                    obs.error(error);
                }
            );
            });
        }
    
        static getHeaders() {
            let headers = new Headers();
            headers.append("Content-Type", "application/json");
            headers.append("SESSION-ID", this.sessionId);
            // headers.append("Authorization", BackendService.appUserHeader);
            return headers;
        }
    }
    

    There is DbService

    import { Injectable } from "@angular/core";
    import { Observable } from "rxjs/Observable";
    import 'rxjs/add/observable/of';
    import "rxjs/add/operator/do";
    import "rxjs/add/operator/map";
    import "rxjs/add/observable/throw";
    import "rxjs/add/operator/catch";
    import 'rxjs/add/operator/toPromise';
    
    var Sqlite = require("nativescript-sqlite");
    
    @Injectable()
    export class DbService {
        private tableActions = "actions";
    
        private columnActionName = "name";
        private columnActionUrl = "url";
    
    
        private database: any;
        constructor() {
            console.log("DbService Constructor");
            (new Sqlite("my_app.db")).then(db => {
                db.execSQL("CREATE TABLE IF NOT EXISTS " + this.tableActions + " (" + this.columnActionName + " TEXT PRIMARY KEY, " + this.columnActionUrl +" TEXT)").then(id => {
                    this.database = db;
                    console.log("DB SERVICE READY");
                }, error => {
                    console.log("CREATE TABLE ERROR", error);
                });
            }, error => {
                console.log("OPEN DB ERROR", error);
            });
        }
    
    
        public getActionUrl(key: string):Observable<any>{
        return new Observable(observer => { 
            if (key === "getApiMap") {
                observer.next("/getApiMap");
                observer.complete();
                return;
            }
            console.log("getActionUrl :" + key);
            this.database.all("SELECT * FROM " + this.tableActions).then(
                rows => {
                    console.log(rows);
                    observer.next(rows[0][this.columnActionUrl]);
                    observer.complete();
                }, error => {
                    console.log("SELECT ERROR: getActionUrl: ", error);
                    observer.error(error);
                })
        });
        }
    }
    

    And there is my AppComponent which makes http requests...

    //some imports
    
    export class AppComponent {
        public resp: Observable<ModelMainGetApiMapRes>;
        public constructor(private bs: BackendService, private db: DbService) {
    let req = new ModelMainGetApiMapReq()
        bs.sendPost("getApiMap", req, false).subscribe(
            (res: ModelMainGetApiMapRes) => {
                console.log("getapimap response received!");
                console.log(JSON.stringify(res));
                console.log("apimap version:" + res.api_version);
    
            },
            err => {
                 console.error("error!", err);
            }
        );
     }
    
    //some functions
    }
    

    the console output of app.component is

    CONSOLE LOG file:///app/shared/backend.service.js:61:20: sendPost:
    CONSOLE LOG file:///app/shared/backend.service.js:66:28: key:  getApiMap
    CONSOLE LOG file:///app/shared/backend.service.js:67:28: url:  http://blabla.com/getApiMap
    CONSOLE LOG file:///app/app.component.js:55:36: getapimap response received!
    CONSOLE LOG file:///app/app.component.js:56:36: {"_isScalar":false,"source":{"_isScalar":false},"operator":{}}
    CONSOLE LOG file:///app/tns_modules/tns-core-modules/profiling/profiling.js:10:16: ANGULAR BOOTSTRAP DONE. 7805.849
    CONSOLE ERROR file:///app/tns_modules/@angular/core/bundles/core.umd.js:1486:24: ERROR Error: Uncaught (in promise): TypeError: undefined is not an object (evaluating 'res.api_version')