Обновление значения в наблюдаемой без разрешения предыдущих операторов - PullRequest
0 голосов
/ 10 октября 2018

Вот что я пытаюсь сделать: у меня есть список пользователей, поступающих из асинхронного источника данных.Источник возвращает Observable<User[]>.Затем список отображается в шаблоне через канал Angular async следующим образом.

<ng-template #loading>Loading...</ng-template>
<ul *ngIf="users$ | async; let users; else loading">
  <li *ngFor="let user of users">{{user.name}}</li>
</ul>

Список пользователей доступен для поиска.Используя реактивные формы, я связал событие изменения поля поиска следующим образом (с this.resolve, выполняющим асинхронную задачу с подписью (text: string) => Observable<User[]>):

search = new FormControl('');
users$ = this.search.valueChanges
  .pipe(startWith(this.search.value))
  .pipe(debounceTime(250))
  .pipe(distinctUntilChanged())
  .pipe(switchMap(this.resolve));

Это прекрасно работает.Входные данные поиска отклоняются, и при изменении входных данных поиска запускается новый асинхронный запрос для разрешения пользователей.

Теперь у меня возникла проблема.Пользователи могут меняться на стороне клиента и должны быть обновлены в списке без повторного выполнения асинхронной задачи.Моя попытка использовать другую операцию конвейера в функции обновления приводит к повторному выполнению this.resolve.

update(user: User): void {
  this.users$ = this.users$
    .pipe(map(users => {
      // replace user in users
      return users;
    }));
}

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

Ответы [ 2 ]

0 голосов
/ 10 октября 2018

В дополнение к предложению Мартина merge() я бы также "поделился" результатом пользователей, чтобы предотвратить повторный запуск resolve.

RXJS будет проходить через все каналы каждый раз, когда добавляется подписка,Думайте о них как о преобразователях, где «хранилище данных» является начальным Subject (this.search.valueChanges).Поэтому каждый раз, когда вы подписываетесь, RXJS будет проходить через ваши каналы, чтобы получить преобразованное значение.

Итак, вы хотите посмотреть на RXJS share() или shareReplay(1).Что они будут делать, так это внутренне создадут new Subject() (в случае share()) или new ReplaySubject(1) (в случае shareReplay(1)) для хранения вашего преобразованного значения в новом «хранилище данных», если хотите.

search = new FormControl('');
otherUsers$ = new Subject();
users$ = merge(
  otherUsers$,
  this.search.valueChanges.pipe(
    startWith(this.search.value)),
    debounceTime(250)),
    distinctUntilChanged()),
    switchMap(this.resolve))
  )
).pipe(share());

Subject против ReplaySubject зависит от того, хотите ли вы сохранить значение, даже если нет подписки.(Это моя интерпретация, основанная на моем собственном использовании.) Я нашел shareReplay(1) полезным в тех случаях, когда у меня есть подписки, такие как obs.pipe(first()).subscribe(...).Однако одна из ваших подписок - это AsyncPipe в DOM, так что вы, вероятно, в порядке с share(), но попробуйте сами.

0 голосов
/ 10 октября 2018

Вы можете использовать Subject и объединить его в цепочку users$ после switchMap.

otherUsers$ = new Subject();

users$ = this.search.valueChanges
  .pipe(
    startWith(this.search.value),
    debounceTime(250)),
    distinctUntilChanged()),
    switchMap(this.resolve),
    merge(otherUsers$),
  );

Затем вы можете позвонить next() и избежать обхода всей цепочки:

otherUsers$.next([{}, {}, {}, ...]);
...