1

I have this code:

import {Injectable, ExceptionHandler, SkipSelf, Host, Optional} from     '@angular/core';
import {ToastNotification} from '../toast-messages/toastNotification.service';

export class UIError extends Error {
    constructor (private toastMessage: string) {
        super();
        this.toastMessage = toastMessage;
    }
}

export class MyUIError extends UIError {}
export class AnotherError extends UIError {}

export class _ArrayLogger {
    res = [];
    log(s: any): void { this.res.push(s); }
    logError(s: any): void { this.res.push(s); }
    logGroup(s: any): void { this.res.push(s); }
    logGroupEnd() {};
}

export class ConsoleLogger {
    log(s: any): void {console.log(s);}
}

@Injectable()
export class CustomExceptionHandler extends ExceptionHandler {
    constructor(private logger: ConsoleLogger, private toast: ToastNotification) {
        super (new _ArrayLogger(), true);
    }

    call(exception: any, stackTrace = null, reason = null) {
        let self = this;
        if (exception.originalException instanceof UIError) {
            self.toast.Error(exception.originalException.toastMessage);
        } else {
            this.logger.log(exception);
        }
    }
}

When I try to run this, I have problem with toast: ToastNotification. The error I get is:

zone.js:260Uncaught EXCEPTION: Error during instantiation of ApplicationRef_! (ApplicationRef -> ApplicationRef_).
ORIGINAL EXCEPTION: Cannot instantiate cyclic dependency! (ExceptionHandler -> ToastNotification)
ORIGINAL STACKTRACE:
Error: DI Exception
    at CyclicDependencyError.BaseException

I inject this component in the boostrap also. How could this be solved?

Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
adam nowak
  • 785
  • 2
  • 9
  • 17
  • 1
    Does that mean `ToastNotification` as `ExceptionHandler` as constructor parameter? – Günter Zöchbauer Jun 02 '16 at 13:19
  • Yes, it has a reference to a AppRef, which in its tree has some ExceptionHandler component :( what would be the best approach to deal with the problem ? – adam nowak Jun 02 '16 at 13:32
  • I guess to not inject `ExceptionHandler` to `ToastNotification` There is no way for DI to cope with cyclic dependencies. This is technically not possible for constructor injection. You could create the instances outside of Angular and resolve the cyclic dependency imperatively by only using a property on `ToastNotification` where `ExceptionHandler` is assigned after instantiation and then provide all related instances using `provide(..., {useValue: ...})` but that is quite ugly. – Günter Zöchbauer Jun 02 '16 at 13:38
  • See also http://stackoverflow.com/questions/37594658/how-to-get-rid-of-this-hack/37596409#37596409 – Günter Zöchbauer Jun 02 '16 at 15:42

2 Answers2

1

After seeing this question How to Inject my service to ExceptionHandler I think this could be a solution for your problem. If you control one of the classes of the cyclic depenencies, you can just inject the Injector to the constructor and then acquire the instance you actually want:

constructor(injector:Injector) {
  setTimeout(() => this.someService = injector.get(SomeService));
}
Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
1

I still had the same issue when using injector in constructor. What solved it for me was moving the injection part to the call function.

@Injectable()
export class AppExceptionHandler extends ExceptionHandler {
  private router:Router;
  private toaster:ToastsManager;

  constructor(private injector: Injector) {
    super(new _ArrayLogger(), true);
  }

  call(exception:any, stackTrace?:any, reason?:string):void {
    this.getDependencies();
    console.log(exception);

    if(exception.status === 401){
      // Show login
      this.router.navigate(['/login']);
    }

    // Get error messages if http exception
    let msgs = [];
    if(exception instanceof Response){
      msgs = this.getMessagesFromResponse(exception);
    }else{

      // Otherwise show generic error
      msgs.push('Something went wrong');
    }

    // Show messages
    msgs.forEach((msg) => this.toaster.error(msg));

    super.call(exception, stackTrace, reason);
  }

  private getDependencies(){
    if(!this.router){
      this.router = this.injector.get(Router);
    }
    if(!this.toaster){
      this.toaster = this.injector.get(ToastsManager);
    }
  }

}
Henri Toivar
  • 376
  • 4
  • 6