diegosasw
diegosasw

Reputation: 15684

Restangular error interceptor - how to pass a custom object to a controller that does not know about restangular?

I am stuck with the approach I am taking probably due to my lack of knowledge about angular promises VS restangular promises, etc. I have an AngularJs application with TypeScript (although typescript is mostly irrelevant here and the same applies to any javascript). These are the players:

  1. controller: it gets injected a service, through this service the controller can send a POST to an API
  2. service: it wraps restangular. The idea is that this service does not expose any restangular functionality to the controller. It abstracts the controller from knowing how to save an item. It has a method that accepts an object and returns an angular promise.

    export interface IRegistrationService {
        addRegistration(model: registration.BusinessRegistration): ng.IPromise<void>;
    }
    
  3. Restangular error interceptor: it handles Http Responses with status 400 coming from an API because they are validation errors and transforms them in a custom object. The idea is that eventually the controller can either succeed saving an item (posting it through the service) or get a validation error (that comes from this interceptor).

This is what I have so far:

The restangular error interceptor

restangularProvider.setErrorInterceptor((response: restangular.IResponse, deferred: ng.IDeferred<any>, responseHandler: any) => {
     if (response.status === 400) {
         let validationError: myTsd.IApiValidationErrors = getAsValidationError(response.data);
            // How to pass this validationError as an errorCallback to the controller?
            //deferred.notify(validationError); 
            //deferred.reject(validationError); //this stops the chain
            //return true; // if error not handled. But where to put the validationError?
            //return false; // if error handled. But where to put the validationError?
        }
    });

The service that abstracts the controller from knowing anything about restangular Notice that it should return an angular promise, not a restangular promise.

public addRegistration(model: registration.BusinessRegistration): ng.IPromise<void> {
     return this.restangular.all("registration")
              .post<registration.BusinessRegistration>(model)
                .then(() => {
                    console.log("Registration posted successfully");
                }, (error: any) => {
                    //if I get the object here, how to make it available in the errorCallback of the controller caller?
                }, (notify: any) => {
                    //if I get the object here, how to make it available in the errorCallback of the controller caller?
                });
 }

The controller that uses that service but knows nothing about restangular

//public static $inject = ["app.services.RegistrationService"];
//.. controller code
this.registrationService.addRegistration(this.model)
      .then(() => {
            console.log("model posted successfully in remote API")
       }, (error: myTsd.IApiValidationErrors) => {
            // if there was any validation error I need the object here
            console.log(error);
       });

How should I chain everything? My "only" requirements are:

Thanks a lot!

I don't fully understand the docs here https://github.com/mgonto/restangular#seterrorinterceptor and whether there is something else other than notifying or rejecting that I could do.

Upvotes: 0

Views: 790

Answers (1)

Roamer-1888
Roamer-1888

Reputation: 19288

Restangular's .setErrorInterceptor() is a rather odd beast, which, as far as I can gather, won't do what you want it to do.

It can be made to sense error code(s) (eg your 400) and do stuff when that condition arises, but has no further ability other than to return false (block) or return anything else (not block).

  • The non-blocking action allows the promise chain to take its natural, unintercepted course.
  • The blocking action inhibits both the error path and the success path of the promise chain.

Therefore think of .setErrorInterceptor() as a "selective blocker", not a "filter" or a "catch", and contrast it with promise.catch() behaviour, by which :

  • an error state can be converted to success by returning some value/object,
  • the error can be rethrown, or some new error can be thrown, keeping the promise chain on the error path.

The inability of .setErrorInterceptor() to propagate anything other than the original error seems to mitigate against it in favour of a named "catch handler" (eg. getAsValidationError() or a function that wraps getAsValidationError()) that can be included wherever relevant. That should give you the feature you require.

The only problem I can foresee is getting the catch handler to recognise the "400" condition - possibly simple - requires research.

Don't get too hung up on Angular promises versus Restangular. They should inter-operate.

Upvotes: 1

Related Questions