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

时间:2019-05-21 12:29:28

标签: angular typescript rxjs rxjs5 rxjs6

我在DataService(v7)项目中有一些同级组件和一个Angular,并且在以下情况下调用方法:

TicketComponent添加票据并在reloadTickets中调用TicketListComponent方法,类似地FileComponent添加文件并通过{{ 1}},如下所示:

DatasService.ts:

reloadFiles

TicketComponent:

FileListComponent

文件组件:

DataService


当我对这两个方法使用单个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); } } 时,两个方法同时被调用。我的意思是由于这两个方法都是通过getEventSubject()方法进行订阅的,因此reloadTickets()方法还会触发DataService中的reloadFiles(),因为这两个方法都使用同一主题(eventSubject)。我知道创建另一个{ {1}}和ngOnInit(): void { this.dataService.getEventSubject().subscribe((param: any) => { this.reloadTickets(); }); } 方法可以解决问题,但是如果我应该为所有独立方法调用执行此操作,或者是否有更聪明的方法通过使用单个ngOnInit(): void { this.dataService.getEventSubject().subscribe((param: any) => { this.reloadFiles(); }); } 来解决问题,我感到困惑如下所述:

BehaviorSubject subscriber gets same next() element multiple times

您能在这种情况下发布正确的用法吗?

更新:

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

EventProxyService:

BehaviorSubject

CommentComponent: 添加评论后,从ListComponent调用方法:

BehaviorSubject

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

getEventSubject

2 个答案:

答案 0 :(得分:1)

是的,这里有一个更聪明的动态创建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)
          );
 }
}

2. / TicketComponent


// 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 
});

}

3. / FileComponent


// 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 
});
}

因此,如果要发送票证组件的数据,则 发送票证数据的组件

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

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

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

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

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

因此,基本上,我们仅创建一个BehaviorSubject,但是BehaviorSubject包含多个对象,这些对象存储我们的数据,在您使用的情况下,我们使用密钥来访问数据,我们使用的密钥名称为{{ 1}}和file

答案 1 :(得分:1)

我很难知道您实际上要实现什么,但是..

首先,不要使用这种构造,因为它会创建无限循环:

this.dataService.getEventSubject().subscribe((param: any) => {
    this.reloadTickets();
});

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

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

// Updates the observable
this.addTicket() {
  this.dataService.addTicket()
}

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

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

export interface File {
  name: string;
  type: 'gif' | 'jpg' | 'png';
}

将类型添加到“可观察对象”后,您会发现实际上需要两个主题。

// 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);

此外,我应该将它们公开,以方便访问而无需使用get()。您只需注入服务并调用可观察对象即可访问它。

constructor(
  private dataService: DataService
)

this.dataService.ticket$

需要将其设为私有时,应使用:

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

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

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

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

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

this.dataService.ticket$.pipe(takeUntil(this.destroy$)).subscribe(tickets => {
  // Do something
})

底线:按照正确的模式进行操作,可以减少很多问题/错误。