单实例Angular 2+通知服务/错误处理程序

时间:2018-08-01 13:57:49

标签: angular error-handling module singleton angular6

我对Angular 2+还是很陌生,所以如果我离开的话,请多包涵。我的目标是要在整个应用程序中使用共享的通知服务。在大多数情况下,我都能正常工作。这是一个懒惰的模块结构,因此为了不存在多个实例问题(花了一段时间让我弄清楚了这个问题),我必须为我的SharedModule类添加一个forRoot()方法,如下所示:

export class SharedModule {
 static forRoot(): ModuleWithProviders {
  return {
    ngModule: SharedModule,
    providers: [
      NotificationService,
      {
        provide: ErrorHandler,
        useClass: ErrorHandlerService
      }]
  };
}

}

然后,将SharedModule添加到AppModule中,调用forRoot():

@NgModule({
 declarations: [...],
 imports: [
  ...
  SharedModule.forRoot()
  ...
 ],
 providers: [...],
 bootstrap: [...]
})
export class AppModule { }

我很高兴看到通知服务在我的惰性模块上运行良好,只需将SharedModule添加到延迟加载的模块中,然后从组件中进行notificationService调用。

问题出在全局错误处理程序(在SharedModule的forRoot()中提供,请参见上文),它看起来像,它可能正在创建第二个通知服务实例,但没有工作正常。

我想首先,我是否正确构造了这个结构?如果是这样,我在做什么错,以便通知服务行为异常?

以下是错误处理程序服务的代码:

     import { ErrorHandler, Injectable, Injector } from '@angular/core';
import { Router } from '@angular/router';
import { HttpErrorResponse } from '@angular/common/http';
import { NotificationService } from './notification.service';

@Injectable()
export class ErrorHandlerService implements ErrorHandler {
  constructor(private injector: Injector) { }

  handleError(error: Error) {
    const notificationService = this.injector.get(NotificationService);
    const router = this.injector.get(Router);
    if (error instanceof HttpErrorResponse) {
      if (!navigator.onLine) {

      } else {
        console.log('Http error!');
        notificationService.error('Error!', error.message);
      }
    } else {

    }
    console.error('It happens: ', error);
  }
}

同样,从组件调用通知服务工作正常,从错误处理程序服务调用时的行为完全不同。我得到的行为类似于我从SharedModule模块中添加forRoot()方法之前从组件中获得的行为,因此是多个实例的理论。

任何帮助表示赞赏。谢谢

---编辑---

我创建了Stackblitz,以显示从组件调用通知服务与从错误处理程序服务调用通知服务时的不同行为。显示通知按钮按预期显示了该通知,但是在发出错误的http调用时,请注意,第一次单击时,该通知不会显示,并且如果继续播放它,有时会突然消失,或根本不消失,而使用“显示通知”按钮则无法做到这一点。

Click here for Stackblitz

2 个答案:

答案 0 :(得分:0)

如果使用Injectable装饰器的grunt.registerTask('buildJson', function(name) { let config = grunt.config.get('buildJson'); // Get the config options from gruntInitConfig for our task. let options = name ? (config[name] || config) : config; // If there isn't a config option that matches, use the object itself function genJson(path, baseUrl = '') { function checkIfFile(fPath) { try { if (fs.lstatSync(fPath).isFile()) { return true; } else { return false; } } catch (e) { if (e.code == 'ENOENT') { return false; } else if (e.code == 'ENAMETOOLONG') { return false; } else { console.error(e); } } } var json = JSON.parse(fs.readFileSync(path, { encoding: 'utf8' })); return JSON.stringify(json, function(key, value) { if (checkIfFile(baseUrl + value)) { return JSON.parse(genJson(baseUrl + value)); } else { return value; } }, 2); } let files = grunt.file.expand(options.srcFiles); for (let file in files) { let srcPath = files[file]; // Determine the output path to write our merged json file. let outputPath = path.join(options.destDir, path.basename(srcPath)); let destJson = genJson(srcPath, options.baseUrl); grunt.file.write(outputPath, destJson); // Write to disk. } }); 属性构建简单的通知服务,则只会得到一个实例。您无需创建sharedModule。

然后可以将通知服务注入到ErrorHandler服务中。

providedIn

但是根据您发布的代码,我想我缺少关于该代码增加复杂性的目的的关键点吗?

答案 1 :(得分:0)

花了几个小时研究这个问题之后……当您抛出http异常时,似乎UI并未接受更改。

您可能不得不问另一个更熟悉变更检测的人,以确定确切的原因...,但是可以解决此问题。

在通知组件中:

import { Component, OnInit, ChangeDetectorRef } from '@angular/core';

  constructor(private notificationService: NotificationService, private cd: ChangeDetectorRef) { }

  ngOnInit() {
    this.notificationService.getNotification().subscribe((notification: Notification) => {
      if (notification) {
        this.show = true;
        this.notification = notification;
        this.cd.detectChanges();        // <- Add this line
        // the rest of your code here
      }
    });
  }

这里是另一篇文章,提供了更多信息,并提供了一些有关更改检测的文章的链接:How to trigger a change event manually - angular2

如果您刚刚起步,也可以考虑使用NgRx,它可以帮助您管理状态和通知。