0

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:

  • the logic to create that object is in a central place like the setErrorInterceptor, and it should distinguish between http responses 400 or any other. If the response is neither 2xx or 400 it can handle the error or pass it to the service that uses restangular. It doesn't matter
  • the service that uses restangular must allow the controller to either succeed or have a callbackError with the custom validation error object. It abstracts the controller from everything else.

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.

0

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.