Должен ли я использовать несколько BehaviorSubject для разных подписок? - PullRequest
2 голосов
/ 21 мая 2019

У меня есть несколько родственных компонентов и DataService в моем проекте Angular (v7), и я вызываю методы в следующем сценарии:

TicketComponent добавляет тикет и вызывает reloadTickets метод в TicketListComponent и аналогично FileComponent добавляет файл и вызывает метод reloadFiles в FileListComponent через DataService, как показано ниже:

DatasService.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);
    }
}

Компонент Ticket:

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

FileComponent:

ngOnInit(): void {
    this.dataService.getEventSubject().subscribe((param: any) => {
        this.reloadFiles();
    });
}

Когда я использую один BehaviorSubject для этих двух методов, оба метода вызываются одновременно, когда вызывается один из них.Я имею в виду, что Поскольку они оба подписаны с помощью метода getEventSubject (), методы reloadTickets () также запускают reloadFiles () в DataService, поскольку оба они используют одну и ту же тему (eventSubject). Я знаю, что создаю другую BehaviorSubject и getEventSubject метод решает проблему, но я смущен, если я должен сделать это для всех независимых вызовов метода или если есть более разумный способ исправить проблему с помощью одного BehaviorSubject, как упомянуто ниже:

Подписчик BehaviorSubject получает один и тот же элемент next () несколько раз

Не могли бы вы опубликовать правильное использование для этого сценария?

Обновление:

Наконец, я использовал следующий подход для вызова разных методов между различными компонентами, используя один BehaviorSubject .

EventProxyService:

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

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

ListComponent: Инициируется с помощью метода reloadComment () в CommentComponent:

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
    }
}

Ответы [ 2 ]

1 голос
/ 23 мая 2019

Мне трудно понять, чего вы на самом деле пытаетесь достичь, но ..

Во-первых, никогда не используйте эту конструкцию, потому что она создает бесконечный цикл:

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';
}

Как только вы добавляете типы в 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);

Кроме того, я должен сделать их общедоступными, чтобы иметь легкий доступ без необходимостиget().Вы можете просто получить к нему доступ, введя службу и вызвав наблюдаемое.

constructor(
  private dataService: DataService
)

this.dataService.ticket$

Когда вам нужно , чтобы сделать их приватными, вы должны использовать:

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

С этой конструкцией вы можете читать наблюдаемые в каждой службе / компоненте , но только обновлять их в содержащей службе .

Еще одна вещь, которую вы всегда должны делать, это завершить наблюдаемые в вашем компоненте, в противном случае вы сохраняете открытую подписку навсегда:

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

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

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

Итог: если вы будете следовать правильным шаблонам, вы получите многоменьше вопросов / ошибок.

1 голос
/ 21 мая 2019

Да, есть один более разумный способ динамического создания 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 содержит несколько объектов, которые хранят нашиданные, и мы получаем доступ к данным с помощью ключа, в вашем случае у нас есть имя ключа, например file и ticket.

...