106

I am working on an Angular app in which I am making a rest call through HTTP as below:

login(email, password) {
    let headers = new Headers();
    headers.append('Content-Type', 'application/x-www-form-urlencoded');
    let options = new RequestOptions({ headers: headers });
    let body = `identity=${email}&password=${password}`;
    return this.http.post(`${this._configService.getBaseUrl()}/login`, body, options)
    .map((res: any) => {
        let response: any = JSON.parse(res._body);
        if (response.success == 0) {
          Observable.throw(response);  // not working
        } else if (response.success == 1) {
          console.log('success');
          localStorage.setItem('auth_token', 'authenticated');
          this.loggedIn = true;
          return response;
        }
    });
}

Basically I want my component to get response & error in my subscribe call, i.e.

this._authenticateService.login(this.loginObj['identity'],this.loginObj['password']).subscribe(
  (success)=>{      
    this.credentialsError=null;  
    this.loginObj={};  
    this._router.navigate(['dashboard']);    
  },
  (error)=>{
    console.log(error);        
    this.credentialsError=error;     
  }
);

but my API always returns success as it is defined that way.

How can I throw an error message if response.success == 0, so that it will be accessed inside error argument of my subscribe callback?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Bhushan Gadekar
  • 13,485
  • 21
  • 82
  • 131

10 Answers10

134
if (response.success == 0) {
   throw Observable.throw(response);  
 } 

Edit for rxjs 6:

if (response.success == 0) {
   throw throwError(response);  
 } 
abd0991
  • 272
  • 5
  • 18
Jorawar Singh
  • 7,463
  • 4
  • 26
  • 39
122

rxjs 6

import { throwError } from 'rxjs';

if (response.success == 0) {
  return throwError(response);  
}

rxjs 5

import { ErrorObservable } from 'rxjs/observable/ErrorObservable';

if (response.success == 0) {
  return new ErrorObservable(response);  
}

What you return with ErrorObservable is up to you

Kristjan Liiva
  • 9,069
  • 3
  • 25
  • 26
  • 11
    The accepted answer solution is deprecated in rxjs6. Thank you, your one works :) – grreeenn Jul 23 '18 at 14:43
  • 1
    This is the way to go when used within an operator that is expected to return an Observable like `switchMap` or `mergeMap`. When used within `map` you will end up with a nested Observable (you will get an Observable in the success callback of your Observable) which probably isn't what you want. – frido Jul 16 '19 at 10:34
13

with rxjs 6

import { throwError } from 'rxjs';
throwError('hello');
sivy
  • 361
  • 2
  • 7
  • 1
    Care to explain why is that better than the native part of the language i.e. the `throw` keyword? – Endrju Nov 07 '18 at 02:25
  • 2
    @Endrju it's completely different. return throwError will return observable that you can then handle with catchError or inside subsribe. if you throw new Error('') this will bubble up and won't be handled inside observables... – pajics Mar 15 '19 at 23:32
  • Just calling `throwError` didn't work for me. – Doug Domeny Feb 10 '21 at 22:00
12

rxjs 5

Either

throw response;

or

throw Observable.throw(response);
Magelan
  • 189
  • 1
  • 13
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
9

rxjs 7

throwError(() => new Error(response))

more info https://rxjs.dev/deprecations/breaking-changes#throwerror

8

Here is the official example (that emits number 7 and then error 'oops!'):

import { throwError, concat, of } from 'rxjs';

const result = concat(of(7), throwError(new Error('oops!')));
result.subscribe(x => console.log(x), e => console.error(e));

From: https://rxjs-dev.firebaseapp.com/api/index/function/throwError

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Sinisa Rudan
  • 561
  • 8
  • 10
0

Use the catch operator

this.calcSub = this.http.post(this.constants.userUrl + "UpdateCalculation", body, { headers: headers })
   .map((response: Response) => {
      var result = <DataResponseObject>response.json();
         return result;
   })
   .catch(this.handleError)
   .subscribe(
      dro => this.dro = dro,
      () => this.completeAddCalculation()
   );

And handle the error like this:

private handleError(error: Response) {
    console.error(error); // log to console instead
    return Observable.throw(error.json().error || 'Server Error');
}
John Baird
  • 2,606
  • 1
  • 14
  • 33
  • my api never calls catch block. thats the issue I am having. In my response I want to check for certain condition and then throw the error or call the catch block. – Bhushan Gadekar Nov 09 '16 at 16:57
  • I've gotten this error with private handleError(error: Response) __WEBPACK_IMPORTED_MODULE_2_rxjs_Observable__.a.throw is not a function at CatchSubscriber.selector – Nam Le Jan 08 '18 at 09:29
  • 1
    @NamLe you have to import throw in order to use it. `import 'rxjs/add/observable/throw';` – SuffPanda Mar 12 '18 at 14:13
0

Most of my issues were related to the imports, so here's the code that worked for me...

import {_throw} from 'rxjs/observable/throw';
login(email, password) {
...
    return this.http.post(`${this._configService.getBaseUrl()}/login`, body, options)
    .map((res: any) => {
...
        if (response.success == 0) {
           _throw(response);  
        } else if (response.success == 1) {
...
        }
    });
}

This will be the solution if you are facing errors like...

ERROR TypeError: WEBPACK_IMPORTED_MODULE_2_rxjs_Observable.Observable.throw is not a function

Ajay
  • 68
  • 6
0

Usually when you're throwing an error you'll be doing so at the exact moment the problem occurred and you want to raise it immediately, but this may not always be the case.

For instance there is the timeoutWith() operator, which is perhaps one of the most likely reasons you'll need to do this.

results$ = server.getResults().pipe(timeoutWith(10000, ....) )

This takes an 'error factory', which is a function.

 errorFactory = () => 'Your error occurred at exactly ' + new Date()

eg.

results$ = server.searchCustomers(searchCriteria).pipe(timeoutWith(10000, 
              () => 'Sorry took too long for search ' + JSON.stringify(searchCriteria)) )

Note that when using timeoutWith you'll never get the actual server response back - so if the server gave a specific error you'd never see it. This above example can be very useful in debugging, but be sure not to display the error to the end user if you use the above example.

AN error factory is helpful because it doesn't evaluate the code until the actual error occurs. So you can put 'expensive' or debugging operations inside that will get executed when the error is actually finally needed.

If you need to use a 'factory' to create your error somewhere other than in timeout you can use the following.

 EMPTY.pipe(throwIfEmpty(errorFactory)) 
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
0

You can use catchError(error) => console.log(error) inside a pipe

Franco
  • 145
  • 1
  • 9