我的Angular5应用程序在应用程序初始化期间从后端加载配置文件(APP_INITIALIZER)。由于应用程序无法在没有它的情况下运行,我的目标是向用户显示无法加载配置的消息。
@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
constructor( private messageService: MessageService) { }
handleError(error) {
// the AppConfig exception cannot be shown with the growl message since the growl component is within the AppComponent
this.messageService.add({severity: 'error', summary: 'Exception', detail: `Global Exception Handler: ${error.message}`});
throw error;
}
}
AppComponent
类应该在应用加载之前从后端服务加载配置文件:
AppComponent
全局异常处理程序:
index.html
如果无法加载配置文件,则抛出异常,捕获并发现在全局异常处理程序中重新抛出(在console.log()中未被捕获的HTTPErrorResponse)并且加载微调器永远挂起)
由于json
没有加载(这没关系,因为没有配置就不能使用app)而且我的消息/“growl”组件是data
的子组件,我无法向用户显示消息。
有没有办法在此阶段在cArray
页面中显示消息?我不想将用户重定向到与index.html不同的.html页面,因为用户只需在error.html上重新加载/ f5。
答案 0 :(得分:3)
我也没有为一个非常类似的问题找到一个好的解决方案,但作为一种解决方法,我就是这样做的:
使用initialized
变量扩展config类:
@Injectable()
export class AppConfig {
public settings: AppSettings = null;
public initialized = false;
public load() {
return new Promise((resolve, reject) => {
this.http.get(environment.serviceUrl + 'config')
.catch((error: any) => {
this.initialized = false;
resolve(error);
return Observable.throw(error || 'Server error');
})
.subscribe((responseData: any) => {
this.settings = responseData;
this.initialized = true;
resolve(true);
});
});
}
}
在app.component.html
内,我会显示应用或错误消息,具体取决于此变量:
<div *ngIf="!appConfig.initialized">
<b>Failed to load config!</b>
<!-- or <app-config-error-page> -->
</div>
<div *ngIf="appConfig.initialized" #layoutContainer >
<!-- the app content -->
</div>
全局异常处理程序:
@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
constructor( private notificationService: NotificationService) { }
handleError(error) {
this.notificationService.error('Exception', `${error.message}`);
return Observable.throw(error);
}
}
当然,还可以在配置对象中存储具体的错误消息/异常文本,并在需要时将其显示给app.component / error.page.component中的用户。
答案 1 :(得分:1)
我提供的方法略有不同。我认为,如果我们无法正确初始化Angular应用程序,我们不希望它们继续运行,因为它无法正常工作。相反,我们只想向用户显示消息,然后停止。
背景:在我的初始化代码中,我必须以特定顺序发出两个异步请求:
我向“自己的” Web服务器(从其提供Angular应用程序的服务器)发出HTTP请求,以获取托管我的后端API的 second Web服务器的主机名
然后我向第二台Web服务器发出请求以获取更多配置信息。
如果这些请求中的任何一个失败,那么我需要停止并显示一条消息,而不是Angular继续执行引导程序。
这是我的代码之前的简化版本,其中添加了新的异常处理(请注意,我更喜欢使用Promise
和async
/ await
Observable
,但这并不重要):
const appInitializer = (
localConfigurationService: LocalConfigurationService,
remoteConfigurationService: RemoteConfigurationService
): () => Promise<void> => {
return async () => {
try {
const apiHostName = await localConfigurationService.getApiHostName();
await remoteConfigurationService.load(apiHostName);
} catch (e) {
console.error("Failed to initialize the Angular app!", e);
}
};
export const appInitializerProvider = {
provide: APP_INITIALIZER,
useFactory: appInitializer,
deps: [LocalConfigurationService, RemoteConfigurationService],
multi: true
};
该代码会将错误记录到控制台,但随后继续进行引导过程-不是我们想要的。
要修改此代码以(a)显示一条消息,并(b)停止其引导过程,我在console.error
调用之后立即添加了以下3行:
window.document.body.classList.add('failed');
const forever = new Promise(() => { }); // will never resolve
await forever;
第一行只是将“失败”类添加到文档<body>
元素中。稍后我们将看到什么效果。
另外两行await
和Promise
从未解决-换句话说,它们永远等待。这具有使Angular引导程序停止的作用,因此Angular应用程序永远不会出现。
最后的更改是在我的index.html
文件中(浏览器加载的HTML文件,该文件“包含”了Angular应用程序)。这是我的更改之前 的样子:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My App</title>
<base href="/">
</head>
<body>
<app-root>Loading...</app-root>
</body>
</html>
初始化Angular应用程序时,会显示<app-root>
元素中的“正在加载...”文本,并且在引导过程完成后会被应用程序内容替换。
确保“哎呀!”如果无法初始化应用程序,则会显示一条消息,我添加了一个<style>
块,其中包含一些CSS样式以及<app-root>
元素中的一些额外内容:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My App</title>
<base href="/">
<style type="text/css">
#failure-message {
display: none;
}
body.failed #loading-message {
display: none;
}
body.failed #failure-message {
display: block;
}
</style>
</head>
<body>
<app-root>
<h1 id="loading-message">Loading...</h1>
<h1 id="failure-message">Oops! Something went wrong.</h1>
</app-root>
</body>
</html>
因此,现在,默认情况下会显示“正在加载...”消息(如果初始化成功,则将其替换为应用程序内容),但会显示“糟糕!”。如果<body>
元素具有failed
类(这是我们在异常处理程序中添加的类),则会显示一条消息。
当然,您可以比简单的<h1>
标签做得更好-您可以在其中放置任何喜欢的内容。
所有这些的最终结果是浏览器显示“正在加载...”,然后将Angular应用加载或将“正在加载...”文本替换为“哎呀!”。
请注意,URL不会更改,因此,如果您在浏览器中单击重新加载,它将从头开始,并尝试重新加载Angular应用程序-可能正是您想要的。
答案 2 :(得分:1)
在main.ts中,我以这种方式更改了引导程序:
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => {
// error should be logged into console by defaultErrorLogger, so no extra logging is necessary here
// console.log(err);
// show error to user
const errorMsgElement = document.querySelector('#errorMsgElement');
let message = 'Application initialization failed';
if (err) {
if (err.message) {
message = message + ': ' + err.message;
} else {
message = message + ': ' + err;
}
}
errorMsgElement.textContent = message;
});
捕获任何发生的异常,并将消息设置为html元素。该元素在app.root标记的index.html中定义,例如
<app-root><div id="errorMsgElement" style="padding: 20% 0; text-align: center;"></div>
</app-root>
如果引导成功,标记的内容将替换为angular。
答案 3 :(得分:0)
我会更新你的初始化程序捕获而不是抛出toGlobalErrorHandler
this.http.get(environment.serviceUrl + 'config/config')
.catch((error: any) => {
window.location.href = '/relativepath/different.html';
return Observable.throw(error || 'Server error')
})
.subscribe((responseData) => {
this.config = responseData;
this.config['service_endpoint'] = environment.serviceUrl;
resolve(true);
});
答案 4 :(得分:0)
我是这样做的......
// app.module.ts
export function loadAppsettings(http: HttpClient) {
return async () => {
try {
const as = await http
.get<IAppsettings>(`//${window.location.host}/appsettings.json`)
.toPromise();
// `appsettings` is a global constant of type IAppsettings
Object.assign(appsettings, as);
return as;
}
catch (e) {
alert('Exception message...');
throw e;
}
};
}
@NgModule({
// ...
providers: [
// ...
{
provide: APP_INITIALIZER,
useFactory: loadAppsettings,
multi: true,
deps: [HttpClient]
},
]
})
export class AppModule { ... }
希望它有所帮助: - )
答案 5 :(得分:0)
因此,如果要重定向,可以在src文件夹中创建error.html文件。然后在出错后将用户重定向到该文件。
创建error.html后,在angular.json文件中将其添加到资源,这样它就不会加载角度应用程序并重定向它。
"assets": [
{
"glob": "**/*",
"input": "src/assets",
"output": "/assets"
},
{
"glob": "error.html",
"input": "src",
"output": "/"
}
],
然后将您的加载功能更改为
public load() {
return new Promise((resolve, reject) => {
this.http.get(environment.serviceUrl + 'config/config')
.catch((error: any) => {
window.location.href = '/error.html';
return Observable.throw(error || 'Server error');
})
.subscribe((responseData) => {
this.config = responseData;
this.config['service_endpoint'] = environment.serviceUrl;
resolve(true);
});
});
}
答案 6 :(得分:0)
我使用的解决方案是创建一个拦截器和一个错误服务。拦截器将捕获所有HTTP错误以及某些我认为是错误的状态代码。
拦截器示例
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http';
import { Router } from '@angular/router';
import { ErrorService } from '@app/@pages/error/error.service';
import { catchError } from 'rxjs/operators';
import { Observable, throwError } from 'rxjs';
@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
constructor(
private router: Router,
private errorSvc: ErrorService
) { }
intercept(req: HttpRequest<any>, next: HttpHandler) {
return next
.handle(req)
.pipe(
catchError((error: any, resp: Observable<HttpEvent<any>>) => {
if (error instanceof HttpErrorResponse && (error.status === 0 || error.status > 400)) {
this.errorSvc.setHttpError(error);
this.router.navigate(['/error']);
}
return throwError(error);
})
);
}
}
错误服务示例
import { Injectable } from '@angular/core';
import { LocalStorageService } from 'angular-2-local-storage';
import { BehaviorSubject, Observable } from 'rxjs';
import { HttpErrorResponse } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class ErrorService {
private error$: BehaviorSubject<HttpErrorResponse> = new BehaviorSubject(this.localStorageSvc.get<HttpErrorResponse>('error'));
constructor(private localStorageSvc: LocalStorageService) { }
setHttpError(error: HttpErrorResponse): void {
this.localStorageSvc.set('error', error);
this.error$.next(error);
}
getHttpError(): Observable<HttpErrorResponse> {
return this.error$.asObservable();
}
}
所以这是如何工作的,当您尝试通过APP_INITIALIZER加载配置数据时,拦截器将捕获错误。发现错误后,应将应用程序路由到错误页面。错误服务将使用本地存储和rxjs行为主题维护错误状态。错误组件会将此服务注入其构造函数,并订阅该服务以显示错误详细信息。