асинхронный канал не подписывается на Observable Angular

В Angular (v12) у меня есть следующий компонент (с использованием имен фиктивных переменных):

export class DataDetailsComponent {
    data$: Observable<MyDataModel>;

    constructor(dataService: DataService, route: ActivatedRoute, private router: Router) {
        this.data$ =
            combineLatest([dataService.getDataObservable(), router.events])
                .pipe(
                    filter(([,event]) => event instanceof ActivationEnd),
                    tap(([data, event]) => console.log((<ActivationEnd>event).snapshot.queryParams['id'])),
                    map(([data, event]) => data.filter(c => c.id === (<ActivationEnd>event).snapshot.queryParams['id'])[0]),
                    tap(dataItem => console.log(dataItem))
                );
        this.data$.subscribe(); // console.logs don't work without this
    }
}

И его шаблон:

<div *ngIf="data$ | async as data; else loading">
    <img [src]="data.url" [alt]="data.name">
</div>
<ng-template #loading>Loading...</ng-template>

Данные не обрабатываются, а консоль браузера пуста, если я не подпишусь на data$. С другой стороны, когда я размещаю this.data$.subscribe();, консоль получает правильный вывод, но представление по-прежнему пусто (зависает при Загрузка...).

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


person dzenesiz    schedule 28.05.2021    source источник
comment
Может быть, ваш наблюдаемый объект возвращает ложные значения, из-за чего *ngIf оценивается как false?   -  person Allan Juan    schedule 29.05.2021
comment
попробуйте combineLatest([dataService.getDataObservable(), router.events]).subscribe(console.log)), чтобы получить больше информации   -  person noririco    schedule 29.05.2021
comment
@AllanJuan, но когда я подписываюсь вручную, я получаю очень правдивые значения от tap(console.log(...))...   -  person dzenesiz    schedule 29.05.2021
comment
Вероятно, это долгий путь, но стоит попробовать переместить код установки в ngOnInit, чтобы убедиться, что он не связан с каким-то неинициализированным состоянием компонента. А что произойдет, если вы замените реальную наблюдаемую фиктивной (истинной)?   -  person James Y    schedule 30.05.2021
comment
Еще одна мысль. Во-первых, обратите внимание, что подписка в конструкторе и подписка в ngFor будут происходить в разное время и, следовательно, с потенциально разными значениями, возвращаемыми из CombineLatest. Попробуйте еще раз коснуться перед фильтром — когда ngFor подпишется, если combLatest не испускает или он отфильтрован, тогда вы увидите то, что видите. Вы также можете попробовать startWith, чтобы проверить гипотезу.   -  person James Y    schedule 31.05.2021


Ответы (2)


может быть связано с вашим CombineLatest. проверьте, чтобы обе наблюдаемые испускали данные.

ОбъединитьПоследнее определение :

Имейте в виду, что combLatest не будет выдавать начальное значение, пока каждый наблюдаемый объект не выдаст хотя бы одно значение. Это то же поведение, что и withLatestFrom, и это может быть ошибкой, поскольку не будет ни вывода, ни ошибки, но один (или несколько) ваших внутренних наблюдаемых, вероятно, не работает должным образом, или подписка просрочена.

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

person Naeim Fard    schedule 29.05.2021
comment
Я на 100% уверен, что данные есть. Это компонент сведений, доступ к которому осуществляется из списка данных, поэтому данные уже загружены в службу. Кроме того, служба хранит данные в BehaviorSubject<T>, поэтому я вынужден использовать начальное значение в конструкторе. - person dzenesiz; 29.05.2021

В конце концов, это было событие маршрутизатора, которое не позволяло наблюдаемому завершиться. Судя по всему, это работает, если я перенесу навигацию из шаблона в .ts и подпишусь на ActivatedRoute.params вместо Router.events.

Итак, родительский компонент получает:

navigateToDetails(id: string): void {
    this.router.navigate(['/myRoute', id]);
}

вместо использования routerLink в шаблоне:

<a [routerLink]="['/myRoute']" [queryParams]="{id: item.id}">

И затем, на DataDetailsComponent я могу сделать:

constructor(dataService: DataService, route: ActivatedRoute) {
        this.data$ =
            combineLatest([dataService.getDataObservable(), route.params])
                .pipe(map(([data, params]) => data.filter(c => c.id === params['id'])[0]));
    }

Причины, почему на данный момент вне меня.

person dzenesiz    schedule 30.05.2021