Совместно используемые данные между несколькими наблюдаемыми объектами RXJS - PullRequest
0 голосов
/ 08 ноября 2019

Я переписываю некоторый код для использования RXJS и углового асинхронного канала. Код довольно прост, мы собираемся добавлять и удалять элементы из массива. Есть ли лучший способ выполнить манипулирование совместно используемым массивом, чем с помощью набора крана сохранить состояние в BehaviorSubject?

this.listSubject = new BehaviorSubject(['a','b','c']);
this.addSubject = new Subject<string>();
this.addAction$ = this.addSubject.asObservable().pipe(
            withLatestForm(this.listSubject),
            map(([itemToAdd, list]) => {list.push(itemToAdd); return list;}),
            tap((data) => this.listSubject.next(data))
        );
this.removeSubject = new Subject<number>();
this.removeAction$ = this.removeSubject.asObservable().pipe(
            withLatestForm(this.listSubject),
            map(([indexToRemove, list]) => {list.splice(indexToRemove, 1); return list;}),
            tap((data) => this.listSubject.next(data))
        );
this.list$ = merge(this.addAction$, this.removeAction$);

РЕДАКТИРОВАТЬ: код пользовательского интерфейса использует асинхронный канал,

list$ | async 

Ответы [ 4 ]

3 голосов
/ 08 ноября 2019

Примерно так работает:

export class HelloComponent {
  actionSubject = new Subject<Action>();
  action$ = this.actionSubject.asObservable();

  originalList$ = of(["a", "b", "c"]);

  list$ = merge(this.originalList$, this.action$).pipe(
    scan((acc: string[], action: any) => {
      if (action.isDelete) {
        return acc.filter(item => item !== action.item);
      } else {
        return [...acc, action.item];
      }
    })
  );

  onAdd() {
    this.actionSubject.next({ item: "z", isDelete: false });
  }

  onRemove() {
    this.actionSubject.next({ item: "b", isDelete: true });
  }
}

export interface Action {
  item: string;
  // This could instead be an Enum of operations
  isDelete: Boolean;
}

ПРИМЕЧАНИЕ. Я сделал стекаблик вашего кода здесь: https://stackblitz.com/edit/angular-array-processing-deborahk

Я создал один поток действий, который генерировал объект действия с помощьюэлемент для добавления / удаления и является ли действие удалением или нет. (Вы можете изменить это на enum для добавления и удаления)

Затем я использовал scan, чтобы сохранить набор элементов с течением времени и просто добавить или удалить из этого списка элементов.

Ваш код находится в файле app.component.ts

Исправленный код находится в файле hello.component.ts.

Это работает для вашего сценария?

ПРИМЕЧАНИЕ. тем не менее, вам нужно подписаться ... вы не делаете, если вы используете асинхронный канал. Мой интерфейс выглядит так:

<div>{{ list$ | async}}</div>
2 голосов
/ 08 ноября 2019

Вообще говоря, большая часть использования tap касается жестких лайнеров RxJS.

Самая большая проблема, с которой я столкнулся, заключается в том, что вы действительно должны изменять состояние своего приложения (субъекта поведения) с помощью подписки, а не касания. В настоящее время у вас нет подписчиков на list$. Если никто не подпишется, ваш tap никогда не будет работать. С другой стороны, если на list$ будет два подписчика, то ваши касания будут выполняться дважды для каждого события! Вы могли бы смягчить это с помощью publish где-нибудь, но я думаю, что было бы намного чище, если бы вы только что ...

this.listSubject = new BehaviorSubject(['a','b','c']);
this.addSubject = new Subject<string>();
this.addSubject.subscribe(itemToAdd => {
  const currentValue = this.listSubject.value;
  currentValue.push(itemToAdd);
  this.listSubject.next(currentValue);
});
this.removeSubject = new Subject<number>();
this.removeSubject.subscribe(indexToRemove => {
  const currentValue = this.listSubject.value;
  currentValue.splice(indexToRemove, 1);
  this.listSubject.next(currentValue);
});

Второе (немного меньшее) беспокойство, которое у меня есть, заключается в том, что выиспуская свой список из нескольких предметов. Теперь у вас есть три предмета, каждый из которых может выдавать один и тот же список. Это означает, что у вас есть три потенциальных источника правды. Вы также нарушаете CQS (разделение командных запросов). Когда что-то вызывает addItem, ему не нужно захватывать новое значение списка. Вместо этого все, что потребляет список, просто получит обновление, потому что тема вашего списка обновлена.

Дополнительное чтение: в вашем приложении здесь есть состояние (список) и события, которые могут изменять состояние. Или, в более общем смысле, «действия», которые могут изменить ваше состояние. В таком инструменте, как ngrx, вы увидите такие понятия, как «магазины» (ваше состояние) и «действия» (ваши события) для описания этой модели. Предметы поведения - легкая альтернатива чему-то вроде ngrx магазина. По мере того, как ваше приложение становится все более и более сложным, вы можете извлечь немалую пользу от изучения таких инструментов, как ngrx store.

1 голос
/ 08 ноября 2019

Почему бы вам не использовать функции?

list$ = new BehaviorSubject(['a','b','c']);

add(val) {
  this.list$.next([...this.list$.value, val]);
}

remove(val) {
  this.list$.next(...this.list$.value.filter(v => v !== val));
}

В вашем коде неясно, что подписывается на все эти наблюдаемые. Вам понадобятся подписки на ваши наблюдаемые действия, чтобы активировать любую функциональность. Вы мутируете объекты.

0 голосов
/ 08 ноября 2019

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

    this.addAction$ = this.addSubject.asObservable().pipe(
                withLatestForm(this.listSubject),
                map(([itemToAdd, list]) => [...list, itemToAdd]),
                tap((data) => this.listSubject.next(data))
            );

    this.removeAction$ = this.removeSubject.asObservable().pipe(
                withLatestForm(this.listSubject),
                map(([indexToRemove, list]) =>
[...list.slice(0, indexToRemove), ...list.slice(indexToRemove + 1)]),
                tap((data) => this.listSubject.next(data))
            );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...