Могу ли я использовать один BehaviorSubject для нескольких событий?

В Angular 8 я использую следующий подход для обновления страницы Details после добавления новой записи:

EventProxyService

export class EventProxyService {
    private eventTracker = new BehaviorSubject<any>(undefined);

    /* Allows subscription to the behavior subject as an observable */
    getEvent(): BehaviorSubject<any> {
        return this.eventTracker;
    }

    /* Allows updating the current value of the behavior subject */
    setEvent(param: any): void {
        this.eventTracker.next(param);
    }
}

Создатькомпонент:

export class CreateComponent implements OnInit {

    constructor(private eventProxyService: EventProxyService) { }

    triggerAnEvent(param: any): void {
        this.eventProxyService.setEvent(param);
    }
}

DetailsComponent:

export class DetailsComponent implements OnInit {

    subscription;

    constructor(private eventProxyService: EventProxyService) { }

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

    theTargetMethod(param) {
        this.record = param; //update record via new one passed from service
    }

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

Подход работает, как и ожидалось, но иногда происходят похожие события, например. Обновление, которое требует обновления страницы Details. Итак, мне интересно, должен ли я создать новый объект BehaviorSubject (eventTracker), методы getEvent и setEvent для каждого из разных событий, например. Update событие? Насколько я знаю, можно использовать один BehaviorSubject, но может возникнуть проблема, если два разных события передают подписчикам несогласованные данные. Каков правильный подход?


person Community    schedule 05.08.2019    source источник
comment
передать объект с такими полями, как type и payload/data, и управлять этим типом   -  person Damian Pioś    schedule 05.08.2019
comment
@DamianPioś Извините, не могли бы вы немного подробнее рассказать об этом, используя приведенный выше код?   -  person    schedule 05.08.2019


Ответы (1)


Самый простой способ — создать тип CustomEvent (не буду называть его Event, так как этот тип уже используется). Вы можете либо дать ему поле eventType, либо использовать классы, которые расширяются от этого CustomEvent, чтобы различать, какой тип события используется.

1. eventType поле

events.ts

export interface CustomEvent{
   eventType: 'Update' | 'OtherEvent';
   data: any; // Type unsafe, maybe narrow it down
}

Прокси события

export class EventProxyService {
    private eventTracker = new BehaviorSubject<CustomEvent>(undefined);
    getEvent(): BehaviorSubject<CustomEvent> { return this.eventTracker; }
    setEvent(param: CustomEvent): void { this.eventTracker.next(param); }
}

добавлять/получать события

// Services omitted, too long
// add new event
this.eventProxyService.setEvent({
    eventType: 'Update',
    data: {/* Your data here */}
});

// listen to events
this.subscription = this.eventProxyService.getEvent()
    // .filter(param => param.evenType === 'Update') // if you want only specific events
    .subscribe((param: CustomEvent) => {
        if (param.eventType === 'Update') {
            this.theTargetMethod(param);
        } else if (param.eventType === 'OtherEvent') {
            // do something else
        }
    );

2. Классы событий

events.ts

export class CustomEvent {}
export class UpdateEvent extends CustomEvent {
   constructor(
      public newData: Data // Use a specific type and as many fields as you need
   ) {}

}
export class OtherEvent extends CustomEvent {
   constructor(
      public otherData: OtherData// Use a specific type and as many fields as you need
   ) {}
}

Прокси события

export class EventProxyService {
    private eventTracker = new BehaviorSubject<CustomEvent>(undefined);
    getEvent(): BehaviorSubject<CustomEvent> { return this.eventTracker; }
    setEvent(param: CustomEvent): void { this.eventTracker.next(param); }
}

добавлять/получать события

// Services omitted, too long
// add new event
this.eventProxyService.setEvent(new UpdateEvent({
   /* Your data here */
}));

// listen to events
this.subscription = this.eventProxyService.getEvent()
    // .filter(param => param instanceof UpdateEvent) // if you want only specific events
    .subscribe((param: CustomEvent) => {
        if (param instanceof UpdateEvent) {
            this.theTargetMethod(param);
        } else if (param instanceof OtherEvent) {
            // do something else
        }
    );

3. Несколько предметов

Прокси события

export type EventType: 'update' | 'other';

export class EventProxyService {
    // Use BehaviourSubject<SPECIFIC_TYPE> if possible
    private updateEventTracker = new BehaviorSubject<any>(undefined);
    private otherEventTracker = new BehaviorSubject<any>(undefined);

    setEvent(type: EventType, param: any): void { 
        this.getEventTrackerForType(type).next(param);
    }
    getEvent(type?: EventType): BehaviorSubject<any> { 
        return this.getEventTrackerForType(type);
    }

    private getEventTrackerForType(type?:EventType): BehaviorSubject<any> {
        switch(type) {
            case 'update': return this.updateEventTracker;
            case 'other': return this.otherEventTracker;
            // if no type specified, return ALL events as one Observable (bonus)
            default: return merge(
                this.updateEventTracker, 
                this.otherEventTracker
            )
        }
    }

    // alternatively, create setEvent/getEvent methods for each subject specifically (setUpdateEvent/getUpdateEvent/...)
}
person pascalpuetz    schedule 05.08.2019
comment
Большое спасибо за помощь, проголосовал. Это действительно отличный подход, и его можно легко использовать. С другой стороны, я также хотел бы уточнить, следует ли мне использовать новые методы BehaviorSubjects и get-set для каждого типа операций, если я хочу использовать мой подход, описанный выше. Потому что, если у меня есть 4-5 различных типов событий ( обновить, создать и т. д.) В вашем примере мне нужно будет использовать длинный блок if-else. Любая помощь, пожалуйста? - person ; 05.08.2019
comment
Вы можете сделать это, если хотите, но в этом случае у вас будет несколько подписок (или вам нужно будет объединить их обратно в одну, используя слияние, тогда вы вернетесь к исходной точке). Не думайте, что это лучше. Я бы предпочел сделать события как можно более похожими, чтобы вам не нужно было различать их в подписке. - person pascalpuetz; 05.08.2019
comment
@hexadecimal Возможно, если это поможет принять решение, события углового маршрутизатора работают точно так же, как во втором примере. (классы). - person pascalpuetz; 05.08.2019
comment
Конечно, я имею в виду предостережения, как вы упомянули. Если я не ошибаюсь, я могу создать несколько подписок, и в этом случае мне также нужно создать методы get-set для каждой из этих подписок (tracker = new BehaviorSubject‹any›(undefined)). Это правда? - person ; 05.08.2019
comment
Да, это то, что вам нужно. В качестве альтернативы вы можете создать метод с параметром и вернуть правильный наблюдаемый объект. Я добавлю это в качестве примера к моему ответу. - person pascalpuetz; 05.08.2019
comment
Ты молодец!.. Большое спасибо за помощь. Последний вопрос: Как я могу сгруппировать события и создать BehaviorSubject для каждой из этих групп? Я имею в виду, например, что есть два события обновления; один для работника, другой зарплата. В этом случае я думаю, что я должен создать 2 updateTracker, это правда? - person ; 05.08.2019
comment
Всегда пожалуйста! :) Есть много способов сделать это. Одним из способов было бы действительно создать несколько субъектов поведения. Для прослушивания вы можете сгруппировать их, как я сделал с merge. И для настройки вы обычно хотите установить только конкретный - или вам нужно будет логически сгруппировать их, используя метод установки. Tbh, в вашем случае я бы уже переключился на библиотеку управления состоянием, такую ​​​​как @ngxs. Это делает его намного менее сложным. - person pascalpuetz; 05.08.2019
comment
На самом деле есть некоторые библиотеки управления состоянием, но в первую очередь я хочу получить опыт работы с Angular и использовать самые распространенные из них. Позже, конечно, думаю об использовании этого. Помечено как ответ ;) большое спасибо... - person ; 05.08.2019
comment
Как насчет использования одной подписки, как указано в Использование одной переменной подписки с BehaviorSubject? Любая идея об этом? - person ; 19.08.2019