【问题标题】:Single Instance Angular 2+ Notification Service / Error Handler单实例 Angular 2+ 通知服务/错误处理程序
【发布时间】:2019-01-09 03:19:39
【问题描述】:

我对 Angular 2+ 还很陌生,所以如果我离题了,请多多包涵。我的目标是在整个应用程序中使用共享通知服务。在大多数情况下,我让它工作正常。它是一个惰性模块结构,因此为了避免多个实例出现问题(我花了一些时间才弄清楚这一点),我不得不在我的 SharedModule 类中添加一个 forRoot() 方法:

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

}

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

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

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

任何帮助表示赞赏。谢谢

--- 编辑---

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

Click here for Stackblitz

【问题讨论】:

  • 需要共享模块吗?如果您只是构建共享服务并将它们注入您需要的地方,这似乎会更容易/更简单。

标签: angular error-handling module singleton angular6


【解决方案1】:

如果你使用 Injectable 装饰器的providedIn 属性构建一个简单的通知服务,你只会得到一个实例。您无需创建 sharedModule。

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

@Injectable({
  providedIn: 'root'
})
export class ErrorHandlerService {
   // ...
   constructor(private notificationService: NotificationService) {}
}

但是根据您发布的代码,我认为我遗漏了一些关键点,即增加该代码复杂性的目的?

【讨论】:

  • 谢谢黛博拉。这是我尝试过的,我将providedIn添加到服务中并将其从SharedModule中完全删除,包括NotificationComponent。我将 NotificationService 和 NotificationComponent 都直接添加到 AppModule 中,但不幸的是我仍然得到相同的行为:(
  • 您能否构建一个堆栈闪电战来演示该问题,以便我们更好地帮助您?
  • 天哪,我可以试试。这是一个大型项目,但是,是的,这是个好主意,我会尝试掌握基础知识,看看我是否可以重现。如果您或其他任何人有任何其他想法,请务必告诉我。 :)
  • 我们绝对不想要大型项目......只需要两个服务(通知和错误处理)以及一个简单的组件来查看它们的运行情况(并希望能说明您的问题)。跨度>
  • 黛博拉,再次感谢您的帮助。我创建了一个 stackblitz 来展示我得到的行为。我已经更新了原始问题以包含链接。请注意,点击显示通知一次或多次可以正常工作,但是当点击错误http调用按钮时,通知行为奇怪
【解决方案2】:

在花了几个小时查看这个之后......似乎当您抛出 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,它可以帮助您管理您的状态和通知。

【讨论】:

  • 你是最棒的,成功了,谢谢!另外,我已经解决了多次调用订阅的问题,这是一个新手错误:不小心使用了间隔而不是超时。顺便说一句,我想我可能在几年前的一次会议上参加过你的一次演讲,当时的 AngularJS!无论如何,再次感谢,这让我抓狂:)
猜你喜欢
  • 2016-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多