为什么在Angular4/5/6中全局错误处理程序内的烤面包机服务不起作用?

10

我的要求是在应用组件加载之前通过调用两个Rest Api来加载一些数据。如果API返回任何错误,则在Toaster(angular2-toaster)中显示消息。

在加载应用程序组件之前,以下AppLoadService将被执行

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import 'rxjs/add/operator/toPromise';
import { APP_SETTINGS } from 'app/app-settings/settings';

@Injectable()
export class AppLoadService {

constructor(private httpClient: HttpClient) { }

loadLabs(): Promise<any> {
    return new Promise((resolve, reject) => {
        this.httpClient.get(`/api/v1/api/lab`)
            .toPromise()
            .then((res: any) => {
                APP_SETTINGS.labs = res;
                resolve();
            })
            .catch((err: any) => {
                reject(err);
            });
    });
}
/////////////////******************////////////////////////////
getSettings(): Promise<any> {
    return new Promise((resolve, reject) => {
        this.httpClient.get(`assets/settings/config.json`)
            .toPromise()
            .then((config: any) => {
                APP_SETTINGS.loginURL = config["login"];
                console.log(`config.json loaded:: `, APP_SETTINGS);
                resolve();
            })
            .catch((err: any) => {
                reject(err);
            });
    });
 }

我的应用程序模块文件如下所示

    export function createTranslateLoader(http: Http) {
        return new TranslateStaticLoader(http, './assets/i18n', '.json');
    }


    @NgModule({
    declarations: [
        AppComponent, CustomDateRangePickerComponent
    ],
    imports:  [
            // coreModules: //
            BrowserModule,
            BrowserAnimationsModule,
            ToasterModule,
            HttpClientModule,
            FormsModule,
            CommonModule, //<====added

            //thirdPartyModules:
            // ToastyModule,
            BootstrapModalModule,
            //appModules: //
            AppRoutingModule,
            FooterModule,
            ErrorModule,
            AccessDeniedModule,
            NotFoundModule,
            AppLoadModule, //Startupdata before APP loaded
            RouterModule.forRoot([]),
            TranslateModule.forRoot({ provide: TranslateLoader, useFactory: (createTranslateLoader), deps: [Http] })
    ],
    providers: [
        ToasterService,
        StartupService,
        ResponseStatusesService,
        LocalStorageService,
        ApplicationSettingsService,
        LabSelectionService,
        AccountService,
        AuthService,
        AlertService,
        AuthGuard,
        RolesGuard,
        FeaturebasedGuard,
        ErrorLogService,
        {
            provide: ErrorHandler,
            useClass: GlobalErrorsHandler
        },
        {
            provide: HTTP_INTERCEPTORS,
            useClass: AppHttpInterceptor,
            multi: true
        },
        {
            provide: LocationStrategy,
            useClass: HashLocationStrategy
        },
    ],
    exports: [TranslateModule],
    bootstrap: [AppComponent]
    })

    export class AppModule { }

GlobalErrorHandler.ts

@Injectable()
export class GlobalErrorsHandler extends ErrorHandler {
constructor(
    private injector: Injector,
    private errorLogService: ErrorLogService

) {
    super();
    alert('GlobalErrorsHandler');

}
handleError(error: Error | HttpErrorResponse) {
    debugger;
    let toaster = this.injector.get(ToasterService);
    toaster.pop("head", "body");
}
}

AppComponent.html

<toaster-container [toasterconfig]="ang2toasterconfig"></toaster-container>

<router-outlet></router-outlet>

拦截器也存在同样的问题。

            import { Injectable, Injector } from '@angular/core';
            import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse, HttpErrorResponse } from '@angular/common/http';
            import { Observable } from 'rxjs/Observable';
            import 'rxjs/add/operator/do';
            import 'rxjs/add/operator/catch';
            import 'rxjs/add/observable/throw';
            import { ToasterService } from 'angular2-toaster';
            import { AuthService } from 'app/blocks/auth/auth.service';
            import { TranslateService } from 'ng2-translate';
            import { AlertService } from '../../core/services/common';


            @Injectable()
            export class AppHttpInterceptor implements HttpInterceptor {

                constructor(private injector: Injector) { }

                intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
                    debugger;
                    console.log(req);
                    if (!window.navigator.onLine) { // check to see if there's internet
                        // if there is no internet, throw a HttpErrorResponse error
                        // since an error is thrown, the function will terminate here
                        return Observable.throw(new HttpErrorResponse({ error: 'NO_INTERNET' }));
                    } else {
                        // else return the normal request
                        return this.handleResponse(next, req);
                    }


                }
                handleResponse(next, req) {
                    return next.handle(req)
                        .do((ev: HttpEvent<any>) => {
                            if (ev instanceof HttpResponse) {
                            }
                        })
                        .catch((response: any) => {
                            if (response instanceof HttpErrorResponse) {
                                console.log('response in the catch: ', response);
                                this.handleReponseExceptions(response);
                            }
                            return Observable.throw(response);
                        });
                }
                handleReponseExceptions(exception) {
                    let toaster = this.injector.get(ToasterService);
                    let translate = this.injector.get(TranslateService);

                    // TOASTER NOT WORKING AND TRANSLATE NOT WORKING
                    toaster.pop("test","test");

                    this.translate.get(["test", "test"]).subscribe(res => {
                        //NOT FETCHING FROM en.json
                    });

                }

            }

据我所知,toaster.pop('','')方法是在toaster-container加载之前调用的。如何解决这个问题。根组件是App组件,在那里我放置了toaster容器。请建议我解决这个问题的架构。
还有一个问题,在哪里处理不同的错误... 在本地错误处理程序(组件级别)、全局错误处理程序或拦截器中?
例如错误:400、404、500+ ...等
更新1:根据David的评论更改了以下代码,但仍然在控制台中出现“没有Container.....”消息,而且没有显示toaster。
使用"angular2-toaster": "^6.1.0"

enter image description here

enter image description here

这些是在应用组件之前将被触发的API调用这里输入图片描述

enter image description here


当你弹出一个Toast时,控制台是否显示“未初始化任何烤面包机容器以接收烤面包”的错误信息?如果没有,则容器已呈现,但UI未捕获区域更改。 - David L
是的,我在控制台中收到了错误信息。 - Tony
看起来你想在应用程序渲染之前就弹出一个提示。你觉得这可能吗? - David L
根据您的看法,这是不可能的......还有一个问题,当我从应用程序调用第一个API时,如果身份验证失败,则会作为响应返回401。现在我想处理这种情况,比如,显示一个烤面包机,说“授权失败,正在重定向到登录页面”。在哪里应该编写代码?在我的观点中,我应该在globalErrHandler或Interceptor中编写。但是,在上述两种情况下,错误消息都是从toast服务中抛出的。拦截器中也出现了相同的错误。 - Tony
5个回答

10

由于这种情况发生的次数以及造成的困惑,angular2-toaster 的自述文件中有详细说明。

这是因为 Angular 在错误处理程序中处理 UI 调度(或者更确切地说,缺乏处理)引起的。

handleError 函数在 Angular 区域之外执行。您需要显式告诉 Angular 在区域上下文中运行弹出调用。

export class AppErrorHandler implements ErrorHandler {
    constructor(
        private toasterService: ToasterService,
        private ngZone : NgZone) { }

    handleError(error: any): void {
        this.ngZone.run(() => {
            this.toasterService.pop('error', "Error", error);
        });  
    }
}

1
这对我很有用,尝试在拦截器中处理HTTP错误。使用MDBootstrap toast模块时,toast会变成白色,没有文本或格式。我像上面的示例一样将其放入区域中,现在它显示得非常完美。谢谢。 - digthewells
这帮助我在自定义错误处理程序中使由PrimeNG提供的Toast组件正常工作。谢谢! - Magnus Wallström

2

我添加了Java Script Toaster而不是angular,并显示了文本。


你用了哪个JavaScript库来制作弹出框? - JPS

1
这是因为handleError()方法在Angular zone之外执行。这会导致提示框无法正确地工作,因为它上面的变更检测不会运行。在错误处理程序中打开onActivateTick以确保提示框在Angular的zone内运行: 工作示例
@Injectable()
export class GlobalErrorHandler extends ErrorHandler {

    constructor(@Inject(Injector) private readonly injector: Injector) {}

    handleError(error) {
        console.log("Handling error: " + error);
        this.toastrService.error("testing", null, { onActivateTick: true })
    }

    /**
     * Need to get ToastrService from injector rather than constructor injection to avoid cyclic dependency error
     * @returns {} 
     */
    private get toastrService(): ToastrService {
        return this.injector.get(ToastrService);
    }

}

{{链接1:Git问题}}


0

这是一个在Angular 6中工作的示例

app.component.html

<toaster-container [toasterconfig]="toasterconfig"></toaster-container>
<router-outlet></router-outlet>

app.component.ts

import { ToasterConfig } from 'angular2-toaster';
import { CustomToasterService} from '../app/core/_services/custom-toaster.service';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss'],
    providers: [CustomToasterService],
})
export class AppComponent implements OnInit {
    toasterConfig: any;
    toasterconfig: ToasterConfig = new ToasterConfig({
        positionClass: 'toast-bottom-right',
        showCloseButton: true,
        tapToDismiss: false,
        timeout: 5000
    });
    constructor(public settings: SettingsService) {
    }

custom-toaster.service.ts

import { Injectable } from '@angular/core';
import { ToasterService } from 'angular2-toaster';

@Injectable()
export class TooasterService {
    constructor(
        private toasterService: ToasterService,
    ) { }


    public showError(error) {
        this.toasterService.pop('error', error.name, error.Message); 
    }
}

0

在创建 tosterservice 之前,客户错误处理程序将被创建。

但是,在创建错误处理程序时,roster 服务不可用。

为了解决这个问题,在自定义错误处理程序的构造函数中使用注入器,并在 handleerror 方法中使用注入器获取 tosterservice。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接