Search code examples
angulartypescriptangular2-decorators

Not able to implement method decorators for catching http errors in Angular?


Actually I want to catch errors on all http requests using custom decorators.

My actual code looks like this:

  createRecord(data: data) {
    
    return this.httpClient.post(`${this.apiURL}/record/`, data);
  }

I want to convert these kind of functions to something like this:

 createRecord(data: data) {
        
        return this.httpClient.post(`${this.apiURL}/record/`, data)
               .pipe(tap((data)=>console.log(data)),catchError(handleError)));
      }

I know this is possible using http interceptors but I tried it using custom method decorators. My decorator looks like this:

export function CatchHttpError() : MethodDecorator {
    return function ( target : any, propertyKey : string, descriptor : PropertyDescriptor ) {
      const original = descriptor.value;
      descriptor.value = original()
      .pipe(
        tap((data)=>console.log('tap entered: data = ',data)),
        catchError(handleError)
      );
      return descriptor;
    };
  }

And then I decorate the function like this:

 @CatchHttpError()
  createRecord(data: data) {
    
    return this.httpClient.post(`${this.apiURL}/record/`, data);
  }

But the problem here is that it tries to execute the function then only when I initialize this particular service, not when I actually call createRecord method. How do I modify the method decorator to achieve this result?


Solution

  • If you want the decorator to alter the behavior of the method it's applied on, you need to override the original method from within the decorator :

    export function CatchHttpError() : MethodDecorator {
        return function (target : any, propertyKey : string, descriptor : PropertyDescriptor ) {
          const original = descriptor.value;
          // override the method
          descriptor.value = function(...args: any[]) {
                // Calling the original method
                const originalResults = original.apply(this, args);
                return originalReults.pipe(
                    tap((data) => console.log('tap entered: data = ',data)),
                    catchError(handleError)
                );
          }
    }
    

    Note that it's important to define the override with the function keyword and not an arrow function to be able to use the this of the class context.