我有一个显示通知的组件。它有一个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失败的通知组件显示。
答案 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;
}