簡體   English   中英

Angular 5 Rxjs Subject.subscribe()未在多個組件中觸發

[英]Angular 5 Rxjs Subject.subscribe() not triggering in multiple components

我正在使用rxjs和subject更新我的兩個組件。

我正在訂閱服務中的主題,但是在主題上調用.next方法時,它僅更新我的組件之一。

該應用程序包括一個WebsocketService用來初始化一個websocket連接,一個NotificationService用來使用WebsocketService連接到后端並發送/接收通知。

我有一個NotificationComponent,可以在其中創建新的通知。 在此組件中,我訂閱了NotificationService中的Subject,並在更新時顯示通知。 這工作正常,消息到達后端,並在當前具有連接的所有瀏覽器中得到更新。

對我來說,下一步是在HeaderComponent中顯示此通知。 我在此處注入NotificationService並訂閱了相同的Subject,但是在發送通知時,不會觸發HeaderComponents訂閱。 console.log消息永遠不會顯示在控制台中。

WebSocketService

import { Injectable } from '@angular/core';
import { ReplaySubject, Subject, Observable, Observer } from 'rxjs/Rx';

@Injectable()
export class WebsocketService {

  constructor() { }

  private subject: ReplaySubject<MessageEvent>;

  public connect(url): ReplaySubject<MessageEvent> {
    if (!this.subject) {
      this.subject = this.create(url);
      console.log("Successfully connected: " + url);
    }
    return this.subject;
  }

  private create(url): ReplaySubject<MessageEvent> {

    //create connection
    let ws = new WebSocket(url);

    //define observable
    let observable = Observable.create(
      (obs: Observer<MessageEvent>) => {
        ws.onmessage = obs.next.bind(obs);
        ws.onerror = obs.error.bind(obs);
        ws.onclose = obs.complete.bind(obs);
        return ws.close.bind(ws);
      });

    //define observer
    let observer = {
      next: (data: Object) => {
        if (ws.readyState === WebSocket.OPEN) {
          console.log("---sending ws message---");
          ws.send(JSON.stringify(data));
        }
      }
    };

    return ReplaySubject.create(observer, observable);
  }
}

NotificationService

import { Injectable } from '@angular/core';
import { Observable, Subject, ReplaySubject, BehaviorSubject } from 'rxjs/Rx';
import { WebsocketService } from './websocket.service';
import { Notification } from './../model/notification'

const NOTIFICATION_URL = 'ws://localhost:8080/Kwetter/socket'


@Injectable()
export class NotificationService {

  public _notification: ReplaySubject<Notification>;

  constructor(websocketService: WebsocketService) {

    this._notification = <ReplaySubject<Notification>>websocketService
      .connect(NOTIFICATION_URL)
      .map((response: MessageEvent): Notification => {
        let data = JSON.parse(response.data);
        return {
          sender: data.author,
          message: data.message
        }
      });
  }

  sendMessage(notification) {
    console.log("---calling .next()---");
    this._notification.next(notification);
  }
}

NotificationComponent

import { Component, OnInit } from '@angular/core';
import { NotificationService } from '../services/notification.service';
import { UserService } from '../services/user.service';
import { Notification } from './../model/notification';

@Component({
  selector: 'app-notifications',
  templateUrl: './notifications.component.html',
  styleUrls: ['./notifications.component.css']
})
export class NotificationsComponent implements OnInit {

  notification: Notification;
  text: string;

  constructor(private notificationService: NotificationService, private userService: UserService) {

    if (this.notification == null) {
      this.notification = new Notification("", "");
    }
    notificationService._notification.subscribe(notification => {
      console.log("---notification has been updated---")
      this.notification = notification;
    });
  }

  sendMsg() {
    let newNot = new Notification(this.userService.getUser(), this.text);
    this.notificationService.sendMessage(newNot);
  }

  ngOnInit() {
  }

}

HeaderComponent

    import { Component, OnInit, OnDestroy } from '@angular/core';
import { UserService } from '../../services/user.service';
import { NotificationService } from '../../services/notification.service';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
import { Profile } from '../../model/profile';
import { User } from '../../model/user';
import { Notification } from '../../model/notification';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit, OnDestroy {

  private notification: Notification;
  private loggedIn = false;
  private user: User;

  private subscription: Subscription;

  constructor(private userService: UserService, private router: Router, private notificationService: NotificationService) {

    console.log("---constructor headercomponent---");
    console.log(this.notification);

    this.notificationService._notification.subscribe(notification => {
      console.log("---header notification has been updated---");
      this.notification = notification;
    });

    if (this.notification == null) {
      this.notification = new Notification("", "");
    }

    this.subscription = this.userService.profile$.subscribe(user => {
      this.user = user;
      if (user !== null) {
        this.loggedIn = true;
      }
      else this.loggedIn = false;
    });

    this.loggedIn = userService.isLoggedIn();
    this.user = userService.getUser();
  }

  logout() {
    this.userService.logout();
    this.router.navigate(['home']);
  }

  home() {
    this.router.navigate(['home']);
  }

  myProfile() {
    console.log("click");
    this.router.navigate(['profile', this.userService.getUser().id]);
  }

  getLoggedIn(): void {
    this.loggedIn = !!this.userService.isLoggedIn();
  }

  ngOnInit() {
    this.getLoggedIn();
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }

}

通過使用路由器出口來顯示NotificationComponent,並且總是通過選擇器標簽來顯示標頭組件,但是我認為這並不重要。

 <div> <app-header></app-header> <div class="content"> <router-outlet></router-outlet> </div> </div> 

我發現以下線程,建議在事件觸發后我訂閱的情況下使用ReplaySubject(我不認為是這種情況,但無論如何我都嘗試過)。 這沒用。

另外,我只有一個app.module聲明提供程序。 由於兩個組件都使用相同的代碼,為什么.subscribe只在NotificationComponent中工作?

角度2:可觀察/訂閱未觸發

控制台視圖

您看到的行為與RxJS的工作方式以及流的創建方式有關。 讓我們看一下WebsocketService

let observable = Observable.create(
  (obs: Observer<MessageEvent>) => {
    ws.onmessage = obs.next.bind(obs);

obs對於每個訂閱都是新的,但是ws始終相同。 因此,當您第二次在NotificationComponent中進行NotificationComponentonmessage回調僅針對該訂閱調用next 因此,只有該組件才能接收消息。

您可以通過在NotificationComponent注釋掉notificationService._notification.subscribe來進行驗證。 然后, HeaderComponent將接收消息。

一種簡單的解決方案是在NotificationService添加share運算符:

this._notification = <ReplaySubject<Notification>>websocketService
  .connect(NOTIFICATION_URL)
  .map((response: MessageEvent): Notification => {
    let data = JSON.parse(response.data);
    return {
      sender: data.author,
      message: data.message
    }
  })
.share();

這意味着將共享.share()上游的訂閱,即(obs: Observer<MessageEvent>) => { ws.onmessage = obs.next.bind(obs); 將僅被調用一次,並且兩個組件都將接收消息。

順便說一句:RxJs提供對websockets的支持 您可以使用Observable.webSocket(url);創建流Observable.webSocket(url); 並擺脫一些代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM