我应该为不同的订阅使用多个 BehaviorSubject 吗?

Jac*_*ack 5 rxjs typescript rxjs5 angular rxjs6

我的(v7) 项目中有一些同级组件和 a DataService,我Angular将方法称为以下场景:

TicketComponent在via中添加ticket和callsreloadTickets方法TicketListComponent,类似地在via中FileComponent添加file和callsreloadFiles方法,如下所示:FileListComponentDataService

数据服务.ts:

export class DatasService {

    private eventSubject = new BehaviorSubject<any>(undefined);

    getEventSubject(): BehaviorSubject<any> {
        return this.eventSubject;
    }

    reloadTickets(param: boolean) {
        this.eventSubject.next(param);
    }

    reloadFiles(param: any) {
        this.eventSubject.next(param);
    }
}
Run Code Online (Sandbox Code Playgroud)

票务组件:

ngOnInit(): void {
    this.dataService.getEventSubject().subscribe((param: any) => {
        this.reloadTickets();
    });
}
Run Code Online (Sandbox Code Playgroud)

文件组件:

ngOnInit(): void {
    this.dataService.getEventSubject().subscribe((param: any) => {
        this.reloadFiles();
    });
}
Run Code Online (Sandbox Code Playgroud)


当我BehaviorSubject对这两种方法使用 single时,当调用其中一种方法时,会同时调用这两种方法。我的意思是,由于它们都通过 getEventSubject() 方法订阅,因此 reloadTickets() 方法也会触发 DataService 中的 reloadFiles(),因为它们都使用相同的主题 (eventSubject)。我知道创建另一个BehaviorSubjectgetEventSubject方法解决这个问题,但我很困惑,如果我要做到这一点对所有的独立方法调用,或者如果有一个更聪明的方式来解决通过使用单一的问题BehaviorSubject如下所述:

BehaviorSubject 订阅者多次获得相同的 next() 元素

您能否发布此场景的正确用法?

更新:

最后,我使用了以下方法,以便使用单个 BehaviorSubject在不同组件之间调用不同的方法。

事件代理服务:

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable()
export class EventProxyService {

    private eventTracker = new BehaviorSubject<any>(undefined);

    getEvent(): BehaviorSubject<any> {
        return this.eventTracker;
    }

    setEvent(param: any): void {
        this.eventTracker.next(param);
    }
}
Run Code Online (Sandbox Code Playgroud)

CommentComponent:从ListComponent调用方法添加注释后:

import { EventProxyService } from './eventProxy.service';

export class CommentComponent implements OnInit {
    constructor(private eventProxyService: EventProxyService) {}        

    public onSubmit() {
        //...
        this.reloadComment(true);
    }

    reloadComment(param: boolean): void {
        this.eventProxyService.setEvent(param);
    }
}
Run Code Online (Sandbox Code Playgroud)

ListComponent:通过 CommentComponent 中的 reloadComment() 方法触发:

import { EventProxyService } from './eventProxy.service';

export class ListComponent implements OnInit {

    subscription;

    constructor(private eventProxyService: EventProxyService) {}

    ngOnInit() {
        this.subscription = this.eventProxyService.getEvent().subscribe((param: any) => {
            this.listComment(param);
        });
    }

    // Multi value observables must manually unsubscribe to prevent memory leaks
    ngOnDestroy(): void {
        this.subscription.unsubscribe();
    }

    listComment(param) {
        //retrieve data from service
    }
}
Run Code Online (Sandbox Code Playgroud)

Yas*_*ami 5

是的,有一种更智能的方法来BehaviorSubject动态创建一个例子,这里就是例子。我希望它能帮助你。

1./DatasService.ts

interface Event {
  key: string;
  value: any;
}


@Injectable({
  providedIn: 'root'
})

export class Broadcaster {

// subject 
protected _eventsSubject = new BehaviorSubject<any>(undefined);
  constructor() {
  }
broadcast(key: any, value: any) {
    this._eventsSubject.next({ key, value }); // here we are setting the key and value of our subject
}

on<T>(key: any): Observable<T> {
    return this._eventsSubject.asObservable()
        .pipe(
            filter(e => e.key === key),
            map(e => e.value)
          );
 }
}
Run Code Online (Sandbox Code Playgroud)

2./ 票务组件


// this is a component which consume the same BehaviorSubject but we are getting a value from "ticket" key

import { Broadcaster } from '../BrodcastService.service';
export class ComponentOne implements OnInit {
constructor(private broadcaster: Broadcaster) { }

someFunction() {

//"ticket" is our key name. so we are getting a value of that key only 

this.broadcaster.on('ticket').subscribe(response => { 
 console.log(response); // here you are getting the data from the other component 
});

}
Run Code Online (Sandbox Code Playgroud)

3./ 文件组件


// this is a component which consume the same BehaviorSubject but we are getting a value from "file" key

import { Broadcaster } from '../BrodcastService.service';
export class componentTwo implements OnInit {
constructor(private broadcaster: Broadcaster) { }

someFunction() {

//"file" is our key name. so we are getting a value of that key only 

this.broadcaster.on('file').subscribe(response => { 
 console.log(response); // here you are getting the data from the other component 
});
}

Run Code Online (Sandbox Code Playgroud)

因此,如果您想发送票证组件的数据,那么 发送票证组件数据的组件

import { Broadcaster } from '../BrodcastService.service';
export class ComponentOne implements OnInit {
constructor(private broadcaster: Broadcaster) { }

someFunction() {
         this.broadcaster.broadcast('ticket', 'data for ticket');
}

Run Code Online (Sandbox Code Playgroud)

为文件组件发送数据的组件

import { Broadcaster } from '../BrodcastService.service';
export class ComponentOne implements OnInit {
constructor(private broadcaster: Broadcaster) { }

someFunction() {
         this.broadcaster.broadcast('file', 'data for file');
}

Run Code Online (Sandbox Code Playgroud)

因此,基本上我们只创建一个BehaviorSubject,但BehaviorSubject包含多个对象来存储我们的数据,并且我们通过使用密钥来访问数据,在您的情况下,我们的密钥名称类似于fileticket


Jef*_*aal 3

我很难知道你到底想实现什么目标,但是..

首先,切勿使用此构造,因为它会创建无限循环:

this.dataService.getEventSubject().subscribe((param: any) => {
    this.reloadTickets();
});
Run Code Online (Sandbox Code Playgroud)

当值更改时,您可以访问组件中的新值。您应该只在操作数据后更新可观察值,例如:

// Reads the observable
this.dataService.getEventSubject().subscribe((param: any) => {
    this.populateForm();
});

// Updates the observable
this.addTicket() {
  this.dataService.addTicket()
}
Run Code Online (Sandbox Code Playgroud)

接下来,您应该始终键入变量,例如:

export interface Ticket {
  artist: string;
  price: number;
}

export interface File {
  name: string;
  type: 'gif' | 'jpg' | 'png';
}
Run Code Online (Sandbox Code Playgroud)

一旦将类型添加到 Observable,您就会发现实际上需要两个主题。

// As a convention, It's recommended to use singular form, and add a $.
public ticket$ = new BehaviorSubject<Ticket[]>(null);
public file$ = new BehaviorSubject<File[]>(null);
Run Code Online (Sandbox Code Playgroud)

另外,我应该将它们公开,以便无需get(). 您可以通过注入服务并调用可观察对象来简单地访问它。

constructor(
  private dataService: DataService
)

this.dataService.ticket$
Run Code Online (Sandbox Code Playgroud)

当您需要将它们设为私有时,您应该使用:

private _ticket$: Subject<Ticket[]> = new BehaviorSubject<Ticket[]>(null);
public ticket$ = this._ticket$.asObservable();
Run Code Online (Sandbox Code Playgroud)

通过这种构造,您可以读取每个服务/组件中的可观察对象,但只能在包含的服务中更新它们

您应该始终做的另一件事是完成组件中的可观察量,否则您将永远保持开放订阅:

private destroy$ = new Subject<any>();

ngOnDestroy() {
  this.destroy$.next();
  this.destroy$.complete();
}

this.dataService.ticket$.pipe(takeUntil(this.destroy$)).subscribe(tickets => {
  // Do something
})
Run Code Online (Sandbox Code Playgroud)

底线:当您遵循正确的模式时,您会遇到更少的问题/错误。