Как показать данные предыдущего наблюдаемого до тех пор, пока новый наблюдаемый не выдаст значение

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

<button  (click)="getData(1)" >getUser1</button>
<button style="margin:50px;" (click)="getData(2)" >getUser2</button>
<div>------------------------------------------</div>
<div *ngIf="userData$ |async as user">
data is{{  user | json}}</div>

и этот наблюдаемый userData $ становится новым каждый раз, когда пользователь нажимает кнопку 1 или 2.

  getData(id) {

   this.userData$ = this.getDataFromBackend(id);
  }

 getDataFromBackend(id) {
    //delay added to simulate network delay
    // fake backend call but i have to use real one in actual project
  return of(this.dataSource[id]).pipe(delay(1000));
  }

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

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

  I can not user loader here meanwhile.
  I know i can do something like 
  let subject = new Subject();
  let userObservable$ = subject.asObservable()
  and use this observable in the html
  and the subscribe to these observable form getDataFromBackend() here in the class and from 
  subscription do the subject.next() and it will send the updated value
  but this does not seem the best way as it do the manual subscription in the component

Ниже приведена ссылка на stackblitz, показывающая проблему.

https://stackblitz.com/edit/angular-ivy-d9nsxu?file=src%2Fapp%2Fapp.component.ts


person Darpan    schedule 27.05.2020    source источник
comment
Почему бы просто не создать статическую переменную let user, а затем превратить запрос в обещание, которое обновляет пользователя по завершении? this.getDataFromBackend(id).toPromise().then( data => (this.user = data)). Нет необходимости в асинхронном канале, просто отобразите пользователя.   -  person Z. Bagley    schedule 27.05.2020


Ответы (4)


Вот способ сделать это без подписки вручную:

app.component.ts

export class AppComponent  {

  userData$ :Observable<any>;
  dataSource = {1:{user:'user1', id:1}, 2: {user:'user2', id:2}};

  private userSrc = new Subject<number>();

  ngOnInit () {
    this.userData$ = this.userSrc.pipe(
      startWith(1),
      switchMap(id => this.getDataFromBackend(id)),
    )
  }

  getData(id) {
    this.userSrc.next(id);
  }

  getDataFromBackend(id) {
    return of(this.dataSource[id]).pipe(delay(1000));
  }
}

StackBlitz.

person Andrei Gătej    schedule 27.05.2020
comment
Спасибо @Andrej, это сработало. Мне нужно внести некоторые изменения, так как я не хотел загружать данные изначально, поэтому вместо использования startwith и switchMap я использовал flatMap. Прикрепленный stackBlitz stackblitz.com/edit/ show-prev-data-urf7jm? file = src / app / - person Darpan; 28.05.2020

Я бы использовал один из двух вариантов:

Вариант 1

Вы создаете другой Observable (loading$ или state$) в своем DataSource (Service, я полагаю?), Который предоставляет либо логическое указание, загружается он или нет, либо (что еще лучше) «состояние» (перечисление), способное указать, если оно: в исходном состоянии »,« загружен »,« загружен »и т. д.

Вариант 2

Вы можете изменить контент, который проходит через ваш userData$ Observable. Я бы предположил, что Observable будет посредником в следующей структуре данных:

{
  user: { ... }, // Containing the user data that has been loaded
  state: "Loaded",
}

(при загрузке пользовательских данных) и:

{
  user: { ... }, // Containing the previously loaded user's data
  state: "Loading",
}

(при загрузке пользовательских данных)

Ну, честно говоря, я бы, скорее всего, использовал систему управления состоянием, похожую на redux, такую ​​как:

Но это другая история

person Anders    schedule 29.05.2020
comment
... запись пользователя в структуре JSON (вариант 2), естественно, будет нулевой (или неопределенной), если ни один пользователь не был загружен. - person Anders; 29.05.2020

Вы можете добавить блок else к блоку *ngIf. Попробуйте следующее

<div *ngIf="userData$ |async as user; else elseBlock">
  data is {{ user | json }}
</div>
<ng-template #elseBlock>
  Loading...
</ng-template>

Я изменил ваш Stackblitz

person Michael D    schedule 27.05.2020
comment
Как я уже упоминал выше, я пока не могу здесь загрузчик пользователя. - person Darpan; 27.05.2020
comment
Вы можете обойти это, используя дополнительное логическое значение. С точки зрения пользователя, лучше использовать загрузчик, чем показывать предыдущие данные. Загрузчик показывает пользователю, что его ввод подтвержден и данные загружаются. Если старые данные сохраняются после ввода пользователя, они могут оказаться неуверенными и в конечном итоге могут привести к нескольким одновременным щелчкам. - person Michael D; 27.05.2020

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

Возможно, вы можете подписаться на вызов getDataFromBackend и перезаписать userData $, когда будет возвращен результат:

  userData$ = new Subject();
  dataSource = {1:{user:'user1', id:1}, 2: {user:'user2', id:2}};

  getData(id) {
    this.getDataFromBackend(id).subscribe(res => this.userData$.next(res));
  }
person hvassup    schedule 27.05.2020
comment
Как я уже упоминал в последнем разделе, эта ручная подписка не кажется отличным способом, но, наконец, если я не найду никакого способа, я думаю, что пойду только на это. - person Darpan; 27.05.2020