如何显示动态Angular 7组件时发生的错误

时间:2019-05-31 13:56:17

标签: angular

我有一个显示通知的组件。它有一个ViewChild和一个ViewContainerRef

基于通知的类型,我显示了不同的组件来代替该viewChild。这是根据通知类型创建和添加组件的代码:

private loadInappNotificationItem(messageType: MessageType): void {
  const inappNotificationItemComponent: Type<IInappNotificationItemComponent>
    = this.inappNotificationItemMapping.get(messageType);
  if (inappNotificationItemComponent === undefined) {
    this.hasCustomComponent = false;
    return;
  }

  const componentFactory: ComponentFactory<IInappNotificationItemComponent> =
    this.componentFactoryResolver.resolveComponentFactory(inappNotificationItemComponent);

  this.hasCustomComponent = true;
  const viewContainerRef: ViewContainerRef = this.inappNotificationItemHost.viewContainerRef;
  viewContainerRef.clear();

  const componentRef: ComponentRef<IInappNotificationItemComponent> =
    viewContainerRef.createComponent(componentFactory);

  const component: IInappNotificationItemComponent = componentRef.instance;
  component.notification = this.notification;
}

这可以按预期工作,但是现在我想显示一个备用组件,如果在显示通知组件时出现任何问题(例如,当notification属性的结构错误时)。

为此,我需要能够在某个地方注册一个函数,该函数在由于某些原因显示viewChild失败时会调用该函数,以便我可以将其删除并显示后备。

我知道我可以在包含该组件的模块上注册一个自定义ErrorHandler,并且可以捕获到我想捕获的错误,但是在该处理程序中,我没有引用其viewChild失败的通知组件显示。

3 个答案:

答案 0 :(得分:1)

  

@Neutrosider如果您引用由您的应用程序代码生成的随机js错误,我会做的是将这些组件的尖角钩包装到try {} catch语句中,但这看起来不太好。或创建一些打字稿自定义注释,其行为将类似于

interface ErrorHandledComponent {
    isInErrorState: boolean;
}

export function HandleComponentErrors() {
    return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        const oriiginalHook = target[propertyKey].bind(target);
        target[propertyKey] = (...args) => {
            try {
                oriiginalHook(...args)
            } catch (e) {
                if (isDevMode) {
                    console.error(e);
                }
                target.isInErrorState = true;
            }
        }
    }
}
@Component({
....
template: `<ng-container *ngIf="!isInErrorState">...content<ng-container>
<ng-container *ngIf="isInErrorState">...error content<ng-container>`
})
export class CumponentWithErrorHandling implements {
   isInErrorState: boolean;
   @HandleComponentErrors()
   ngOnInit() {
   ...your custom code
   }
}
  

如果那就是你的话,基本上注释所有有角度的钩子   尝试实现,那么您将得到一个不错的js错误渲染   在您的代码实现中,或者可能进一步分派错误,或者   将其记录到您的后端。希望对您有帮助

答案 1 :(得分:0)

如果您调用API调用之类的任何方法,那么如果您希望看到错误(如果发生),请使用以下格式.....我使用了

this.customerService.deleteAll()
  .subscribe(
    data => {
      console.log(data);
      this.reloadData();
    },
    error => console.log('ERROR: ' + error));

它在浏览器的devTool中的控制台上生成日志

答案 2 :(得分:0)

部分答案

以下答案将适用于components类中发生的所有错误。但是,它不会捕获模板中发生的错误(例如,访问不存在的属性)。 这个答案的灵感来自answer from @Nicu

这是一个类装饰器,它将装饰类的每个方法包装在try-catch中,以便可以在其他地方处理错误

export function CatchErrors(): Function {

  return (decoratedClass): any => {
    // find the names of all methods of that class
    const properties: Array<string> = Object.getOwnPropertyNames(decoratedClass.prototype);
    const methodsNames = properties.filter((propertyName): boolean => {
      return typeof decoratedClass.prototype[propertyName] === 'function';
    });

    // wrap every method in a try-catch
    console.log(properties, methodsNames);
    for (const methodsName of methodsNames) {
      decoratedClass.prototype[methodsName] = new Proxy(decoratedClass.prototype[methodsName], {
        apply: (target, thisArg, argumentsList): void => {
          try {
            return target.apply(thisArg, argumentsList);
          } catch (error) {
            console.warn(`calling ${methodsName} on ${decoratedClass.name} failed`, error);
            for (const callback of decoratedClass.__jsErrorCallbacks) {
              callback(error);
            }
          }
        },
      });
    }

    // add a method to register callbacks, that should be executed if something goes wrong
    decoratedClass.__jsErrorCallbacks = [];
    decoratedClass.prototype.__onJSError = (callback): void => {
      decoratedClass.__jsErrorCallbacks.push(callback);
    };
  };
}

然后我们只需要将该装饰器添加到我们的组件中即可:

import {CatchErrors} from './somewhere';

@CatchErrors()
@Component({
  selector: 'some-component',
  templateUrl: 'some-component.component.html',
  styleUrls: ['some-component.component.scss'],
})
export class SomeComponent {
  /* ...*/
}

最后,我们可以在父组件中做出反应。
(大部分代码是从原始问题中复制的,仅用于上下文):

// THIS PART IS COPIED FROM THE ORIGINAL QUESTION
private loadInappNotificationItem(messageType: MessageType): void {
  const inappNotificationItemComponent: Type<IInappNotificationItemComponent>
    = this.inappNotificationItemMapping.get(messageType);
  if (inappNotificationItemComponent === undefined) {
    this.hasCustomComponent = false;
    return;
  }

  const componentFactory: ComponentFactory<IInappNotificationItemComponent> =
    this.componentFactoryResolver.resolveComponentFactory(inappNotificationItemComponent);

  this.hasCustomComponent = true;
  const viewContainerRef: ViewContainerRef = this.inappNotificationItemHost.viewContainerRef;
  viewContainerRef.clear();

  const componentRef: ComponentRef<IInappNotificationItemComponent> =
    viewContainerRef.createComponent(componentFactory);

  const component: IInappNotificationItemComponent = componentRef.instance;

  // THIS IS THE INTERESTING PART
  // ------------------------
  if (component.__onJSError !== undefined) {
    component.__onJSError((error): void => {
      console.log(`replacing custom component for ${this.notification} with fallback`);
      viewContainerRef.clear();
      this.hasCustomComponent = false;
    });
  }
  // ------------------------

  component.notification = this.notification;
}