【问题标题】:Angular 8: singleton service creating new instances each time it is required on a new fileAngular 8:单例服务在每次需要新文件时创建新实例
【发布时间】:2020-07-03 07:17:06
【问题描述】:

我有一个完整的工作平台,它在服务中使用 Rxjs 行为主体(Observables)来保持整个应用程序的状态。它工作得很好,直到我需要包含 WebSockets 来更新同一帐户中许多用户的信息。 当更新的信息到达用户时,我调用行为主题来检查是否应该更新活动的供应商(用户当前看到的供应商),但是 Observable 总是返回“null”,因为它是一个新的实例可观察的(与用户屏幕上显示的不同)。

为了测试它,我在两个不同的文件中记录了相同的 observable:

//Supplier.service.ts

import { Injectable } from "@angular/core";
import { BehaviorSubject } from 'rxjs';
import { Supplier } from '../../models/supplier.model';
import { UserService } from './User.service';

@Injectable({providedIn: 'root'})
export class SupplierService {
  activeSupplier = new BehaviorSubject<Supplier>(null);

  constructor (
    private http: HttpClient,
    private userService: UserService,
  ) {}

{...}

getOneSupplier(supplierId: string) {
  this.http.get<Supplier>(`${BASEURL}supplier/${supplierId}`)
    .subscribe(supplier => {
        this.activeSupplier.next(supplier);
        this.activeSupplier.pipe(take(1)).subscribe(activeSupplier => {
        console.log('updater:', activeSupplier); // <----------------- here
    });
  });
}

updateSupplierList(changedSupplier) {
    this.activeSupplier.pipe(take(1)).subscribe(activeSupplier => {
      console.log('Socket changing active supplier:', activeSupplier);
        if (activeSupplier._id === changedSupplier._id) {
          this.getOneSupplier(activeSupplier._id);
      }
    });
  }

{...}

}

//Socket.service.js

import { Injectable } from '@angular/core';
import io from './SocketCopier.js';
import { UserService } from './User.service.js';
import { SupplierService } from './Supplier.service.js';

@Injectable({
providedIn: 'root',
})
export class SocketService {
socket;
subs = {};

constructor(
private userService: UserService,
private supplierService: SupplierService
) {
this.supplierService.activeSupplier.subscribe(activeSupplier => {
  console.log('socket:', activeSupplier); // <----------------- and here
});
}

loadSocket(user) {
io.then((result) => {
  if (this.socket) {
    this.socket.close();
  }

  this.socket = result('http://localhost:3000');
  this.socket.on('connect', () => {
    this.socket.emit('login', { account: user.account._id })
  });

  this.socket.on('logged', (msg) => {
    console.log(msg.text);
  });

  this.socket.on('supplierChange', (msg) => {
    this.supplierService.updateSupplierList(msg.data);
  });
});
}

}

//supplier-info.component.ts

import { Component, OnInit, OnDestroy } from '@angular/core';
import { SupplierService } from 'src/app/shared/services/Supplier.service';
import { ActivatedRoute, Router, NavigationEnd } from '@angular/router';
import { Supplier } from 'src/app/models/supplier.model';
import { filter } from 'rxjs/operators';

@Component({
  selector: 'app-supplier-info',
  templateUrl: './supplier-info.component.html',
  styleUrls: ['./supplier-info.component.css'],
})
export class SupplierInfoComponent implements OnInit, OnDestroy {
  supplier: Supplier;
  subs = {};
  activeRoute: string = 'projetos';

  constructor(
    private supplierService: SupplierService, 
    private route: ActivatedRoute,
    private router: Router,
    ) { }

  ngOnInit() {
      this.supplierService.getOneSupplier(this.route.snapshot.params.supplierId);
  }

  {...}

}

代码首先调用Socket.service.ts,后者调用Supplier.service.ts,创建行为主体“activeSupplier”,初始值为“null”。

然后 Socket.service.ts 在其构造函数中订阅“activeSupplier” observable 并按预期返回“null”。

然后生成supplier-info.component.ts,在ngOnInit中它需要供应商信息,调用“this.supplierService.getOneSupplier()”。这会更新记录供应商的“activeSupplier”BehaviorSubject。

这是我从这些日志中得到的结果:

socket: null
updater: {socialMediaUrls: {…}, photoUrl: "", emails: Array(0), skills: Array(0), tags: Array(4), …}

Socket 的 observable 永远不会再次更新,因为它是“supplierService”的一个新实例,并且它不与supplier-info.component.ts 共享同一个“activeSupplier”。应该更新了吧?因为它订阅了同一个 Observable(至少理论上如此)。

我在这里没有看到什么? 提前感谢您的所有帮助!

编辑: 值得一提的是,任何对“activeSupplier” Observable 订阅的新订阅都会返回“null”,因为它是第一个值(即使在第二个带有供应商的 console.log 之后)。

【问题讨论】:

  • 我明白了,您已经在根目录中注入了服务,您是否有可能在功能模块中提供服务?因为这可能会导致为该模块下的组件创建该服务的单独实例。
  • 由于我对 Angular 还是很陌生,我只有一个模块 (app.module.ts) 并且它没有提供任何服务——顺便检查一下。文件中甚至没有对它们的引用。所有服务都通过“@Injectable({providedIn: 'root'})”提供。我还应该去哪里寻找?也许我只是不知道这个“在功能模块中提供服务”可能在哪里。
  • 请参阅此文档angular.io/guide/singleton-services。您的服务实例创建和单例服务范围取决于您提供服务的方式。
  • @PushpikaWan 我已经检查了这个文档,这就是我在我的代码中所遵循的。我所有的服务都是在他们自己的文件中提供的,带有“@Injectable({providedIn: 'root'})”,并且在其他任何地方都没有提供。我刚刚检查了整个项目文件夹中的 Visual Studio Code 搜索,并且在它自己的文件之外没有提供任何内容(对于'root'):(
  • updateSupplierList 在做什么?貌似updateSupplierList是socket接收消息时调用的方法。

标签: angular rxjs observable angular-services behaviorsubject


【解决方案1】:

我找到了! 在许多(许多)调试日志之后,我发现我真的在创建另一个服务实例。为了找出这一点,我记录了 Supplier.service.ts 构造函数。然后我看到两个日志都发生在不同的行中。打开这些文件(chrome inspect > source)我发现它们是不同的(?),所以我尝试在 Sockets.service.ts 中“重写”导入,它成功了!

那是因为我错误地导入了它(末尾带有“.js”)。

从这里:

import { UserService } from './User.service.js';
import { SupplierService } from './Supplier.service.js';

到这里:

import { UserService } from './User.service';
import { SupplierService } from './Supplier.service';

现在一切正常!

【讨论】:

    【解决方案2】:

    阅读您的评论和其中的代码,我认为您可以尝试这样的事情。

    首先,您将从 http 服务收到的 活跃供应商 的值保存在某处,可能在开始时。像这样的

    export class SupplierService {
      activeSupplier = new BehaviorSubject<Supplier>(null);
      activeSupplierValue: Supplier;
    
      constructor (
        private http: HttpClient,
        private userService: UserService,
      ) {}
    ...
    
    getOneSupplier(supplierId: string) {
      this.http.get<Supplier>(`${BASEURL}supplier/${supplierId}`)
        .subscribe(supplier => {
            this.activeSupplierValue = supplier;  // save the data of the active supplier
            ......
        });
      });
    }
    ...
    
    }
    

    然后,您将 活动供应商 与您从套接字收到的任何内容进行比较,如果匹配,您将再次触发 getOneSupplier

    updateSupplierList(changedSupplier) {
       if (activeSupplier._id === changedSupplier._id) {
          this.getOneSupplier(activeSupplier._id);
       }
    }
    

    【讨论】:

    • 我也不工作:(...为了调试它,我做了这个“this.activeSupplierValue = supplier;”并在它之后记录“console.log(this.activeSupplierValue + ' - ' + new Date(Date.now()))”。然后我在“updateSupplierList”中进行了相同的日志记录。结果:(1) [object Object] - Sun Mar 22 2020 23:55:58 GMT+0100 (Central欧洲标准时间)| (2) 未定义 - Sun Mar 22 2020 23:56:06 GMT+0100(中欧标准时间)。
    猜你喜欢
    • 2019-06-21
    • 2022-01-23
    • 1970-01-01
    • 2018-09-11
    • 1970-01-01
    • 2014-08-28
    • 1970-01-01
    • 2014-05-20
    • 1970-01-01
    相关资源
    最近更新 更多