Angular 8: одиночный сервис, создающий новые экземпляры каждый раз, когда это требуется для нового файла - PullRequest
1 голос
/ 22 марта 2020

У меня полностью рабочая платформа, которая использует Rx js Behavior Subjects (Observables) в сервисах для поддержания состояния всего приложения. Он работал довольно хорошо, пока мне не понадобилось включать WebSockets для обновления информации между многими пользователями в одной учетной записи. Когда обновленная информация попадает к пользователям, я вызываю субъект поведения, чтобы проверить, должен ли быть обновлен активный поставщик (того, которого в данный момент видит пользователь), но 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» с «нулем» в качестве начального значения.

Затем Socket.service.ts в своем конструкторе подписывается на наблюдаемую «activeSupplier» и возвращает «null», как и ожидалось.

Затем генерируется supplier-info.component.ts и в ngOnInit требуется для информации о поставщике, вызывая «this.supplierService.getOneSupplier ()». Это обновляет «активныйSupplier» BehaviorSubject, который регистрирует поставщика.

Вот результат, который я получаю из этих журналов:

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

Наблюдаемое сокета никогда не обновляется, так как это новый экземпляр "supplierService", и он не использует тот же "activeSupplier" с supplier-info.component.ts. Это должно быть обновлено, верно? Как подписан на тот же Observable (хотя бы теоретически).

Что я здесь не вижу? Заранее благодарим за вашу помощь!

edit: Стоит сказать, что любая новая подписка на подписку "activeSupplier" Observable возвращает значение "ноль" в качестве первого значения (даже после второго console.log с поставщиком). ).

Ответы [ 2 ]

0 голосов
/ 23 марта 2020

Я нашел это! После многих (МНОГИХ) журналов отладки я обнаружил, что действительно создаю еще один экземпляр службы. Чтобы это выяснить, я зарегистрировался в конструкторе 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';

А теперь все отлично работает!

0 голосов
/ 23 марта 2020

Читая ваш комментарий и код в нем, я думаю, вы можете попробовать что-то вроде этого.

Сначала вы сохраняете где-нибудь значение активного поставщика , которое вы получаете от службы 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);
   }
}
...