Почему служба тостера внутри глобального обработчика ошибок не работает в Angular4/5/6?

Мое требование состоит в том, чтобы загрузить некоторые данные, вызвав Two 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('','') вызывается перед загрузкой контейнера тостера. Как решить эту проблему. Корневой компонент — это компонент приложения, в который я поместил контейнер тостера. Пожалуйста, предложите мне архитектуру для решения этой проблемы.

Еще один, где мне нужно обрабатывать разные ошибки... В локальном обработчике ошибок (уровень компонента) или в глобальном обработчике ошибок или в перехватчике?

Примеры ошибок: 400 404 500+ ... и т.д.

Обновление 1: в соответствии с комментарием Дэвида изменен код, как показано ниже, но по-прежнему нет контейнера..... сообщение приходит в консоль, и тостер не виден

Использование «angular2-toaster»: «^ 6.1.0»

введите описание изображения здесь

введите описание изображения здесь

Это вызовы API, которые будут выполняться перед компонентом приложения введите здесь описание изображения

введите описание изображения здесь


person Abhi    schedule 20.07.2018    source источник
comment
Вы получаете сообщение об ошибке в своей консоли, нет контейнеров тостеров, которые были инициализированы для получения тостов, когда вы выдвигаете тост? Если нет, контейнер визуализируется, но пользовательский интерфейс не улавливает изменение зоны.   -  person David L    schedule 21.07.2018
comment
Да, я получаю сообщение об ошибке в консоли   -  person Abhi    schedule 21.07.2018
comment
Таким образом, кажется, что вы хотите произнести тост еще до того, как ваше приложение отобразится. Как вы ожидаете, что это будет возможно?   -  person David L    schedule 21.07.2018
comment
По вашему мнению, это невозможно... Еще один вопрос: когда я вызываю первый API из приложения, если аутентификация не удалась, в качестве ответа придет 401. Теперь я хочу обработать этот сценарий, например, отобразить тостер с сообщением об ошибке авторизации, перенаправить на страницу входа. Где я должен писать код, на мой взгляд, я должен писать в globalErrHandler или Interceptor. Но в обоих случаях из службы тостов выдается сообщение об ошибке. Такая же ошибка возникает и в Interceptors.   -  person Abhi    schedule 21.07.2018


Ответы (5)


Это задокументировано в README angular2-toaster из-за того, сколько раз это встречается и вызывает путаницу.

Это вызвано тем, как Angular обрабатывает диспетчеризацию пользовательского интерфейса (точнее, ее отсутствие) в обработчиках ошибок.

Функция 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);
        });  
    }
}
person David L    schedule 20.07.2018
comment
Это сработало для меня, пытаясь обработать ошибки http в перехватчике. Используя модуль тостов MDBootstrap, тост появлялся белым, без текста или форматирования. Я поместил его в зону, как в приведенном выше примере, и он отлично отображается. Спасибо. - person digthewells; 07.09.2018
comment
Это помогло мне заставить компонент Toast, предоставленный PrimeNG, работать в пользовательском обработчике ошибок. Спасибо! - person Magnus Wallström; 06.03.2019

Я добавил Java Script Toaster не угловой и показал текст.

person Abhi    schedule 25.07.2018
comment
какую библиотеку Javascript вы использовали для тостера? - person JPS; 08.10.2018

Это происходит потому, что метод handleError() выполняется за пределами зоны Angular. Это приводит к тому, что всплывающие уведомления ведут себя неправильно, поскольку для них не выполняется обнаружение изменений. Включите onActivateTick в обработчике ошибок, чтобы убедиться, что всплывающее уведомление выполняется внутри зоны Angular:

Рабочий пример

@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);
    }

}

проблема с Git

person Patel Romil    schedule 08.09.2019

Обработчик ошибок клиента будет создан перед tosterservice как Но при создании обработчика ошибок служба реестра недоступна

Чтобы решить эту проблему, используйте Injector в конструкторе в пользовательском обработчике ошибок и в методе handleerror получите tosterservice с помощью инжектора.

person Dharmaraj Kavatagi    schedule 20.07.2018

Этот пример работает в 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) {
    }

обычай-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); 
    }
}
person unknow27    schedule 28.08.2018