Реактивная разработка |RxJS асинхронная труба не работает - PullRequest
1 голос
/ 27 сентября 2019

Я пытаюсь изучить реактивную разработку в Angular путем преобразования моих старых методов в сервисе, чтобы вообще НЕ подписываться в Observable.Пока что самым простым методом, который я уже преобразовал, был метод getAll.Теперь я хочу, чтобы, когда я выбрал одну карту в списке, я был перенаправлен на другую страницу, чтобы просмотреть ее детали.Рассмотрим приведенный ниже код, над которым я сейчас работаю.

СЕРВИС

@Injectable({
  'providedIn': 'root'
})
export class CardService {

  private selectedCardIdSubject = new Subject<number>();
  selectedCardIdAction$ = this.selectedCardIdSubject.asObservable();

 /**
   * Retrieve single card based on card id.
   */
  card$ = this.selectedCardIdAction$
    .pipe(
      tap(id => console.log(`card$: ${this._cardsApi}/${id}`)),
      concatMap(id =>
        this._http.get<Card>(`${this._cardsApi}/${id}`)
          .pipe(
            map(card => ({
              ...card,
              imageUrl: `${this._cardImageApi}/${card.idName}.png`
            }))
          )
      ),
      catchError(this.handleError)
    );

  onSelectedCardId(id: number) {
    this.selectedCardIdSubject.next(id);
    console.log(`onSelectedCardId: ${id}`)
  }    
}

КОМПОНЕНТ

@Component({})
export class CardDetailsComponent {
    constructor(private _cardService: CardService,
        private route: ActivatedRoute) { 

        this.selectedCardId = this.route.snapshot.params['id'];
        this._cardService.onSelectedCardId(this.selectedCardId);
    }

    card$ = this._cardService.card$;
}

HTML

<div class="container mb-5" *ngIf="card$ | async as card">
    <p>{{ card.name }}</>
</div>

В приведенном выше коде при перенаправлении мой простой HTML-код не отображает имя выбранной карты.Я использую [routerLink] для перенаправления на странице сведений о карточке.

Компонент списка карточек

<div *ngIf="cards$ | async as cards">
    <div class="col-md-1 col-4 hover-effect" *ngFor='let card of cards'>
      <a class="d-block mb-1">
        <figure>
          <img class="img-fluid img-thumbnail" [src]='card.imageUrl' [title]='card.name' [alt]='card.name'
            [routerLink]="['/cards', card.id]" />
        </figure>
      </a>
    </div>
</div>

Кто-нибудь, кто может просветить меня о том, что здесь происходит?

1 Ответ

2 голосов
/ 27 сентября 2019

Причина, по которой он не работает, заключается в том, что ваша $ card подписана после того, как излучение уже запущено в вашем конструкторе, и в это время представление еще не отображается, а это означает, что канал aysnc не на месте.

Сценарий очень похож на приведенный ниже порядок кодов, поэтому вы не получили обновления

// constructor
const a=new Subject()
a.next('some value')
// rendering
a.subscribe(console.log) // never triggered

Существует два подхода к решению этой проблемы:

  • 1-е кэширует последнее значение с помощью shareReplay(1), поэтому оно всегда выдает последнее значение при подписке

    selectedCardIdAction$ = this.selectedCardIdSubject.asObservable().pipe(shareReplay(1));
    

или использует BehaviorSubject

private selectedCardIdSubject = new BehaviorSubject<number | null>(null);
  • 2-й шаг .next() в ngAfterViewInit - он срабатывает после создания представления и подписки асинхронного канала

Лично я пойду на 1-й подход, результат будет более последовательным, если ваш код не 'у меня много недостатков

...