l oop через BehaviorSubject <> и установить свойство объекта - PullRequest
2 голосов
/ 09 мая 2020

У меня есть событие изменения значения Multi-Select, в котором я хотел бы сначала отфильтровать выбранный объект из BehaviorSubject, а затем установить для свойства IsSelected значение true, а затем для всех оставшихся объектов в потоке установить для свойства IsSelected значение false. Пробовал разные вещи в Интернете, но не нашел решения. Пожалуйста помоги.

Модель:

export class SelectOptions {
  DisplayName: string;
  Value: string;
  IsSelected: boolean;
}

Компонент:

selectOptions$: BehaviorSubject < SelectOptions[] > = new BehaviorSubject<SelectOptions[]>([]);

ngOnInit() {
  this.reportService
    .getFiltersData(this.fieldNameEnum.AicSuites)
    .pipe(
      tap((res) => {
        console.log("--- data from api ---");
        this.selectOptions$.next(res);
        console.log(this.selectOptions$.value); = > this shows proper data
      })
    )
    .subscribe();
}

Событие OnValueChange:

// here data is a array of selected items from the multi-select
// the goal here is to whenever user select any item, i need to set that object IsSelected prop to true and send it to Api later on.
// if user select the same item again, it's actually de-selectiong the item and (data) array will always have selected items. so i need to set IsSelected = false for all remaining items from Observable stream.


markAsSelected(data) {
  data.forEach((selectedItem: SelectOptions) => {
    this.selectOptions$
      .pipe(
   ?? => no idea what to do here.tired several things but getting compilation error.
 )
})}

Любая помощь будет принята с благодарностью. Приветствуется любой другой подход к тому, чтобы сделать то же самое лучшим способом.

Ответы [ 2 ]

1 голос
/ 09 мая 2020

Ваш главный вопрос о том, как изменить состояние в rx js (добавить, удалить, изменить значение, ...). Это может быть достигнуто с помощью оператора scan .

Дополнительная информация: в последующем сканировании мы используем 1. параметр (accumulator) как oldOptions/state и 2. param (value) как функция (fn), которая получает oldOptions/state, возвращает новое / обновленное состояние.

Шаг 1 - что представляет ваше состояние

state: SelectOptions[];

Шаг 2 - какая мутация может быть сделана в вашем состоянии

  • переопределение состояния: в настоящее время, когда this.reportService.getFiltersData (this.fieldNameEnum. AicSuites) переопределяет все ваше состояние. Это происходит в операциях касания со строкой this.selectOptions $ .next (res).
  • мутация состояния: вы хотите изменить свое состояние при вызове markAsSelected (). Вы хотите инвертировать значение IsSelected из selectedOptions.

Шаг 3 - создайте функции, которые изменяют состояние, которое вы определили

Вы увидите использования и почему мы построили его, как на шаге 4

  • переопределение состояния
function stateOverride (newOptions) {
  return function (oldOptions) {
    return newOptions;
}
// shorter alternative syntax for the same function:
const stateOverride = (newOptions) => (oldOptions) => newOptions;

function stateMutation (selectedOptions) {
  return function (oldOptions) {
    // I use the DisplayName to compare the selectedOptions with the oldOptions 
    const selectedDisplayNames = selectedOptions.map(selectedOption => selectedOption.DisplayName)

    /* We map over every option. If this specific option is included in the selectedDisplayNames
    we create a new object with all old values but inverted IsSelected.
    Otherwise we let the oldOption as it is and do not mutate it.*/
    return oldOptions.map(oldOption => selectedDisplayNames.includes(oldOption.DisplayName)
      ? {...oldOption, ...{IsSelected: !=oldOption.IsSelected}}
      : oldOption
  }
}

Шаг 4. Используйте оператор сканирования для создания , обновите свое состояние

private newState$ = this.reportService.getFiltersData(this.fieldNameEnum.AicSuites)

private optionsSelect$ = new Subject();

private selection$ = merge(
  newState$.pipe(map(v => stateOverride(v)),
  optionsSelect$.pipe(map(v => stateMutation(v))
).pipe(
  scan((oldOptions, fn) => fn(oldOptions), [])
  /* This is why we used this weird function syntax (newOptions) => (oldOptions) => in Step 3.
     1. We map the stateOverride/stateMutation to the Observables.
     2. Those return itself another function that takes the oldOptions
     3. In the scan the second param is now a function that takes the oldOptions and returns the updated state/options.*/
)

function markAsSelected(data): void {
  this.optionsSelect$.next(data);
}

К вашему сведению: если вы понимаете этот механизм, вы можете обновить любое состояние в каналах без побочных эффектов таким образом. Если вы понимаете принцип и следуете инструкциям, это будет полезным инструментом для множества различных сценариев использования. Не стесняйтесь спрашивать дополнительную информацию. Я обновляю ответ или могу прокомментировать ниже.

0 голосов
/ 09 мая 2020

попробуйте это,

  markAsSelected(data ) {
    data.forEach((selectedItem: SelectOptions) => {
      this.selectOptions$.asObservable().pipe(map(selectedObj => {
          selectedObj.forEach(obj => {
            if (selectedItem === obj) {
              selectedItem.IsSelected = true;
              console.log('true');
            } else {
              selectedItem.IsSelected = false;
            }

          });

        })
      );
    });
  }
...