为什么多次创建我的@Injectable服务? [英] Why is my @Injectable service created more than once?

查看:70
本文介绍了为什么多次创建我的@Injectable服务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:由于问题被归结为一个不止一次实例化的@Injectable服务,请向下滚动到更新".

Note: Please scroll down to the "Update" as the issue was boiled down to a @Injectable service being instanciated more than once.

我有一个可以为企业服务的解析器:

I have a resolver that loads businesses:

resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any {
  this._logger.debug('Loading application data.');
  return forkJoin([
    this._userService.getMe(),
    this._businessService.getAll()
  ]).pipe(
    tap(([user, businesses]) => {
      this._appState.user = user;
      this._appState.businesses = businesses;
    }),
    finalize(() => this._logger.debug('Application data loaded.'))
  );
}

有一个ApplicationState,其中有两个BehaviorSubject成员_business_businesses:

There is an ApplicationState that has two BehaviorSubject members _business and _businesses:

constructor() {
  this.businessChange = this._business.asObservable();
  this.businessesChange = this._businesses.asObservable();
}

set business(business: BusinessModel) {
  console.log(business);
  this._business.next(business);
}

get business(): BusinessModel {
  return this._business.getValue();
}

set businesses(value) {
  this._businesses.next(value);
  console.log(this.businesses);
}

get businesses(): Array<BusinessModel> {
  return this._businesses.getValue();
}

如您所见,在解析器中设置了所述状态的businesses. business但是取决于路由,并根据该路由在(延迟加载)模块中进行设置:

As you can see, businesses of said state gets set in the resolver. business however depends on the route and gets set in a (lazy-loaded) module depending on that route:

ngOnInit() {

  this._appState.businessesChange.subscribe(
    (b) => {
      console.log('businessesChange');
      console.log(b);
    }
  );

  this._subscriptions.add(
    this._activatedRoute.params
      .subscribe((params) => {
        this._routeParams = params;
        const businessId: number = parseInt(params['businessId'], 10);
        const businesses = this._appState.businesses;
        console.log(`businessId ${businessId}`);
        console.log('available businesses');
        console.log(businesses);
        this._appState.business = businesses.find(b => b.id === businessId);
      })
  );
}

这是相关的日志输出:

2019-08-28T09:11:04.989Z DEBUG [..~df7ced28.js:428] Loading application data.
ngx-logger.js:251 2019-08-28T09:11:04.992Z DEBUG [main.js:398] Fetching /me
application.state.ts:56 Set businesses:
application.state.ts:58 (3) [{…}, {…}, {…}]
ngx-logger.js:251 2019-08-28T09:11:06.781Z DEBUG Application data loaded.
businesses.component.ts:43 businessesChange
businesses.component.ts:44 []
businesses.component.ts:47 businessId 4
businesses.component.ts:48 available businesses
businesses.component.ts:49 []
application.state.ts:46 Set business:
application.state.ts:47 undefined
ngx-logger.js:251 2019-08-28T09:11:08.386Z DEBUG Place is null. No action.

如您所见,businesses被设置并包含三个元素.然后,我们从解析器看到应用程序数据已加载..之后,我们看到_activatedRoute.params订阅说明该路由显示了4businessId,但随后..this._appState.businesses突然是一个空列表-BehaviorSubject的初始值,即使我们稍等片刻将其打印出来.

As you can see, businesses gets set and contains three elements. Then we see that Application data loaded. from the resolver. After that, we see the _activatedRoute.params subscription stating that the route reveals a businessId of 4 but then.. this._appState.businesses is all of a sudden an empty list - the initial value of the BehaviorSubject even though we printed that out a few moments earlier.

由于我也正在登录/调试set方法,因此我可以看到在将该值设置为三个元素的列表后,该值不会不会被更改.

Since I am logging/debuggin the set methods as well, I can see that this value does not get changed after being set to that list of three elements.

我不知道这是怎么回事.可能是什么原因造成的?

I have no idea what's going on here. What could possibly cause this?

我已经尝试过的:

  • 重新启动服务器
  • 使用调试器(但我还是要进行printf-debug调试)

好吧,我将问题归结为不止一次被实例化的ApplicationState:

Okay I have boiled the issue down to the ApplicationState being instanciated more than once:

通过给ApplicationState一个简单的ID(时间戳):

By giving the ApplicationState a simple ID (timestamp):

constructor() {
  this.businessChange = this._business.asObservable();
  this.businessesChange = this._businesses.asObservable();
  this.timestamp = moment().format('MM.SS SSS');
  console.log(`${this.timestamp} ApplicationState created`);
}

并更改setget方法中的日志输出,我得到了:

and changing the log-output in the set and get methods, I get this:

08.09 095 ApplicationState created
ngx-logger.js:251 2019-08-28T09:51:16.098Z DEBUG Loading application data.
ngx-logger.js:251 2019-08-28T09:51:16.099Z DEBUG [main.js:398] Fetching /me
:4200/#/businesses/4/calendar:1 This site does not have a valid SSL certificate! Without SSL, your site's and visitors' data is vulnerable to theft and tampering. Get a valid SSL certificate before releasing your website to the public.
application.state.ts:60 08.09 095 Set businesses:
application.state.ts:62 (3) [{…}, {…}, {…}]
application.state.ts:38 08.14 145 ApplicationState created
ngx-logger.js:251 2019-08-28T09:51:16.161Z DEBUG Application data loaded.
businesses.component.ts:43 businessesChange
businesses.component.ts:44 []
businesses.component.ts:54 businessId 4
businesses.component.ts:55 available businesses
businesses.component.ts:56 []
application.state.ts:50 08.14 145 Set business:
application.state.ts:51 undefined
ngx-logger.js:251 2019-08-28T09:51:16.178Z DEBUG Place is null. No action.
calendar.component.ts:105 

因此,有ApplicationState08.09 095(A)和08.14 145(B)实例.

So there is a 08.09 095 (A) and a 08.14 145 (B) instance of ApplicationState.

A设定了业务,但显然使用了B.

A gets the businesses set but further B is used apparently.

我检查了一下,ApplicationState在我的ApplicationModule中仅导入了一次:

I checked, ApplicationState gets imported only once in my ApplicationModule:

providers: [
  // Services
  ApplicationState,
  ApiServices,
  ApplicationResolver,
]

那为什么会这样呢?这不是辛格尔顿吗?是因为这遍历了延迟加载的模块吗?

So why is this happening? Shouldn't this be Singleton? Is it because this goes over a lazy-loaded module?

但最重要的是:为什么在过去的几周中这项工作奏效了?

But most importantly: Why did this work for the last few weeks?

我现在还添加了providedIn:

@Injectable({providedIn: 'root'})
export class ApplicationState {

但它的行为仍然相同.

推荐答案

好的,答案为什么显然是由于延迟加载.似乎有什么事情要做依赖树的构建或类似的事情.

Okay, the answer why this happened was apparently due to lazy loading. It seems to have something to do how the dependency tree gets build or something like that.

我不知道详细信息,我会接受任何有关此主题的更详细的分析器.

I do not know the details and I would accept any anser which is more detailed about this topic.

无论如何,我认为在 parent 模块中提供服务就足够了.因为有了感觉,不是吗?

Anyway, I thought it should be enough to provide the service in a parent module. Because it makes sence, doesn't it?

事实证明,这确实必须在app.module.ts中提供.我在提供ApplicationState的地方有另一个名为application.module.ts的模块(不应混淆). application.module.ts是延迟加载的,但也有一些再次被延迟加载的路由,例如businesses.module.ts.所以我的层次结构看起来像这样:

Turns out that this really has to be provided in the app.module.ts. I have another module, called application.module.ts (should not be confused) where I provided ApplicationState. application.module.ts is lazy loaded but also has some routes which are again lazy loaded e.g. businesses.module.ts. So my hierarchy looks something like this:

app.module.ts
|_ application.module.ts  // lazy, providers: [ApplicationState]
|  |_ businesses.ts       // lazy, uses ApplicationState too
|_ public-pages.module.ts // not lazy

我需要更改的是设置

providers: [ApplicationState]

app.module.ts中.

这不是理想的imo,但是由于延迟加载是这样工作的,因此我将不得不对其进行处理.

This is not ideal imo but since lazy loading works like this I'll have to deal with it.

显然这是一个已知问题( https://github.com/angular/angular/Issues/12869 ),但堆栈上也有可能的解决方案溢出: https://stackoverflow.com/a/41293784/826983

Apparently this is a known issue (https://github.com/angular/angular/issues/12869) but there is also a possible solution on stackoverflow: https://stackoverflow.com/a/41293784/826983

这篇关于为什么多次创建我的@Injectable服务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆