2
votes

I have some api that returns responses in the format

{
  code: 0, // 0 is Ok, all other values is error
  data: [{...}, {...}, {...}, ...], // data objects
  message: "" // empty when ok or some error message in other cases
}

I try to create communication service for this api

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';

interface IApiResponse<T> {
  code: number;
  data: Array<T>;
  message: string;
}

@Injectable()
export class ApiService {

  constructor( 
    private http: Http 
  ) { }

  apiGet<T>(url: string): Observable<Array<T>> {
    return this.http.get(url)
            .map((res: Response) => res.json())
            //.map((api: IApiResponse<T>) => api.code === 0 ? api.data : Observable.throw(api.message))
            .mergeMap((api: IApiResponse<T>) => api.code === 0 ? api.data : Observable.throw(api.message))
            .catch(err => Observable.throw(err));
  }

}

I would like to raise exception when code field is not equal 0 and return message in error part of subscribe. But when i return Observable.throw in map it returns as data in subscribe not as error. When i try mergeMap instead, exception returns as error in subscribe function as expected but data returns for each object in data array. It's not properly for me. May be i must use some other observable function for this?

Service usage in a component

import { Component, OnInit } from '@angular/core';

interface ITestItem {
  id: number;
  title: string;
}

@Component({
  selector: 'app-main',
  templateUrl: './main.component.html',
  styles: []
})
export class MainComponent implements OnInit {

  constructor(
    private apiService: ApiService
  ) { }

  ngOnInit() {
    this.apiService.apiGet<ITestItem>('/api/test')
      .subscribe(
        // only data when code = 0 or nothing
        data => console.log("data section:", data), 
        // handle http errors and message when code != 0
        error => console.log("error section:", error), 
        () => console.log("api request completed")
      );
  }

}
2

2 Answers

2
votes

The reason why this happens is because mergeMap will convert the result to an Observable if it is possible. Since your data is an array it converts it to an Observable and emits each element contained.

Wrap your data with Rx.Observable.of() in the .mergeMap to keep it as an Array emission

0
votes

You're pretty close, based on the RxJS doc. You need to throw an exception like so:

 Observable.throw(new Error(api.message));

Hope that helps

EDIT more info

Service, make whatever change neccessary for your need.

getTestData(): any {
    return this.http.get("testUrlShouldThrowError")
      .map((api: any) => {
        if (api.code === 0) {
          return <IApiResponse<Test>>api.data
        }
        else {
          return Observable.throw(new Error("Error!"))
        }
      });
  }

Subscription:

this.testService.getTestData().subscribe(
      data => console.log(data.error.message),
    )