Verified Commit fefeeadb authored by Merzough Münker's avatar Merzough Münker
Browse files

feat(error-capture): implement error capture dialog service and refactor error handling

parent 2b3b9a3e
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -33,10 +33,12 @@ export * from './lib/any-http-error/any-http-error.service';
// endregion

// region 
export * from './lib/error-capture-dialog.service';
export * from './lib/error-capture.service';
export * from './lib/error-handler';
export * from './lib/error-interceptor-options';
export * from './lib/http-error-interceptor';
export * from './lib/provide';
export * from './lib/tokens';
export * from './lib/utilities';
// endregion
+77 −0
Original line number Diff line number Diff line
import { ComponentType } from '@angular/cdk/overlay';
import { DOCUMENT } from '@angular/common';
import {
  ApplicationRef,
  createComponent,
  inject,
  Injectable,
  Injector,
  WritableSignal,
} from '@angular/core';
import {
  finalize,
  Observable,
  take,
} from 'rxjs';
import { tap } from 'rxjs/operators';
import {
  ErrorDialogComponent,
  IErrorDialogComponent,
} from './error-dialog/error-dialog.component';
import {
  RXAP_ERROR_DIALOG_COMPONENT,
  RXAP_ERROR_DIALOG_DATA,
} from './tokens';

export interface IErrorCaptureDialogService<Error = any> {
  open(component: ComponentType<IErrorDialogComponent>, errorList: WritableSignal<Error[]>): Observable<void>;
}

@Injectable({ providedIn: 'root' })
export class ErrorCaptureDialogService<Error = any> implements IErrorCaptureDialogService<Error> {

  protected readonly applicationRef = inject(ApplicationRef);
  protected readonly document = inject(DOCUMENT);

  open(component: ComponentType<IErrorDialogComponent>, errorList: WritableSignal<Error[]>): Observable<void> {
    const body = this.document.getElementsByTagName('body')[0];
    const div = this.document.createElement('div');
    body.appendChild(div);
    const componentRef = createComponent(ErrorDialogComponent, {
      hostElement: div,
      environmentInjector: this.applicationRef.injector,
      elementInjector: Injector.create({
        providers: [
          {
            provide: RXAP_ERROR_DIALOG_DATA,
            useValue: errorList,
          },
          {
            provide: RXAP_ERROR_DIALOG_COMPONENT,
            useValue: component,
          },
        ],
      }),
    });
    this.applicationRef.attachView(componentRef.hostView);
    return new Observable<void>(subscriber => {
      const subscription = componentRef.instance.closeDialog.pipe(
        take(1),
        tap(() => {
          subscriber.next();
          subscriber.complete();
        }),
        finalize(() => {
          componentRef.destroy();
          body.removeChild(div);
          div.remove();
        })
      ).subscribe();
      return () => {
        subscription.unsubscribe();
      };
    });

  }

}
+8 −46
Original line number Diff line number Diff line
import { ComponentType } from '@angular/cdk/overlay';
import {
  ApplicationRef,
  createComponent,
  inject,
  Injectable,
  Injector,
  signal,
  WritableSignal,
} from '@angular/core';
import { take } from 'rxjs';
import { tap } from 'rxjs/operators';
import {
  ErrorDialogComponent,
  IErrorDialogComponent,
} from './error-dialog/error-dialog.component';
import {
  RXAP_ERROR_DIALOG_COMPONENT,
  RXAP_ERROR_DIALOG_DATA,
} from './tokens';
  ErrorCaptureDialogService,
  IErrorCaptureDialogService,
} from './error-capture-dialog.service';
import { IErrorDialogComponent } from './error-dialog/error-dialog.component';
import { RXAP_ERROR_CAPTURE_DIALOG_SERVICE } from './tokens';

@Injectable()
export abstract class ErrorCaptureService<Error = any> {

  protected abstract readonly component: ComponentType<IErrorDialogComponent>;
  private readonly applicationRef = inject(ApplicationRef);
  private readonly errorCaptureDialogService = inject<IErrorCaptureDialogService<Error>>(
    RXAP_ERROR_CAPTURE_DIALOG_SERVICE, { optional: true }) ?? inject(ErrorCaptureDialogService);
  private errorList: Array<WritableSignal<Error[]>> = [];

  push(error: Error) {
@@ -40,41 +34,9 @@ export abstract class ErrorCaptureService<Error = any> {
    }
    const newList = signal([ error ]);
    this.errorList.push(newList);
    this.openDialog(newList);
    this.errorCaptureDialogService.open(this.component, newList);
  }

  abstract compare(a: Error, b: Error): boolean;

  openDialog(errorList: WritableSignal<Error[]>) {
    const body = document.getElementsByTagName('body')[0];
    const div = document.createElement('div');
    body.appendChild(div);
    const componentRef = createComponent(ErrorDialogComponent, {
      hostElement: div,
      environmentInjector: this.applicationRef.injector,
      elementInjector: Injector.create({
        providers: [
          {
            provide: RXAP_ERROR_DIALOG_DATA,
            useValue: errorList,
          },
          {
            provide: RXAP_ERROR_DIALOG_COMPONENT,
            useValue: this.component,
          },
        ],
      }),
    });
    this.applicationRef.attachView(componentRef.hostView);
    componentRef.instance.closeDialog.pipe(
      take(1),
      tap(() => {
        componentRef.destroy();
        body.removeChild(div);
        div.remove();
        this.errorList.splice(this.errorList.indexOf(errorList), 1);
      }),
    ).subscribe();
  }

}
+4 −2
Original line number Diff line number Diff line
@@ -4,7 +4,8 @@
  <div *ngIf="data().length > 1" class="controls flex justify-center space-x-2 mt-4">
    <button
      (click)="activeIndex.set(activeIndex() - 1)"
      *ngIf="activeIndex() > 0" class="px-4 py-2 bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-900 hover:bg-gray-300 transition-colors duration-150 rounded">Previous
      *ngIf="activeIndex() > 0" class="px-4 py-2 bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-900 hover:bg-gray-300 transition-colors duration-150 rounded">
      Previous
    </button>
    <button (click)="activeIndex.set(item)"
            *ngFor="let item of displayedButtons()"
@@ -17,7 +18,8 @@
    </button>
    <button
      (click)="activeIndex.set(activeIndex() + 1)"
      *ngIf="activeIndex() < data().length - 1" class="px-4 py-2 bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-900 hover:bg-gray-300 transition-colors duration-150 rounded">Next
      *ngIf="activeIndex() < data().length - 1" class="px-4 py-2 bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-900 hover:bg-gray-300 transition-colors duration-150 rounded">
      Next
    </button>
  </div>
  <div class="flex flex-col overflow-auto">
+2 −7
Original line number Diff line number Diff line
import { HttpErrorResponse } from '@angular/common/http';
import {
  EnvironmentProviders,
  ErrorHandler,
  inject,
  Injectable,
  InjectionToken,
  INJECTOR,
  isDevMode,
  Provider,
  runInInjectionContext,
  StaticProvider,
} from '@angular/core';
@@ -162,10 +164,3 @@ export class RxapErrorHandler implements ErrorHandler {


}

export function ProvideErrorHandler(): StaticProvider {
  return {
    provide: ErrorHandler,
    useClass: RxapErrorHandler,
  };
}
Loading