Reputation: 455
I've got an input with dropdown list that isn't displaying results. It in an ngIf with async. The dropdown list is displayed when I remove the ngIf code so I think it must be either the ngIf check or how I've setup the test observable service. When I press a key entering 1 value nothing is displayed, the second time I enter a key it displays the list.
<div class="format-options" *ngIf="(definitions | async)?.length > 0">
<div *ngFor="let definition of definitions | async" class="search-result">
<div>{{definition.name}}</div>
<div>{{definition.description}}</div>
</div>
</div>
Search Service:
searchTerm(term: string): Observable<Array<ObjectDefinition>>
let observer = new Observable<Array<ObjectDefinition>>((subscription) => {
const timer$ = interval(1000);
timer$.subscribe(() => {
console.log('timer 1 second');
subscription.next(this.objectDefinitions);
subscription.complete();
});
});
return observer;
}
Component:
constructor(private definitionService: DefinitionService) {
this.definitions = this.searchInput.valueChanges
.pipe(
tap(value => console.log('input')),
//startWith(''),
debounceTime(500),
//distinctUntilChanged(),
switchMap(value => this.definitionService.searchTerm(value))
);
}
Upvotes: 2
Views: 1901
Reputation: 3935
You can set the result of the async operation directly in the *ngIf directive like that:
<div class="format-options" *ngIf="definitions | async as defs">
<div *ngFor="let definition of defs" class="search-result">
<div>{{definition.name}}</div>
<div>{{definition.description}}</div>
</div>
</div>
Then no need for the second async pipe.
PS: Never subscribe in your services, pefere using RxJs operators.
Upvotes: 5
Reputation: 71891
I guess the first *ngIf
subscribes through the async
pipe. Once this returns a result, the observable completes, and the *ngFor
tries to susbcribe to an already completed observable which does not replays it's last emit. You can fix this by adding a shareReplay(1)
pipe:
this.definitions = this.searchInput.valueChanges.pipe(
tap(value => console.log('input')),
debounceTime(500),
switchMap(value => this.definitionService.searchTerm(value)),
shareReplay(1)
);
The whole searchTerm
observable looks a bit weirdly constructed though, and you should have a look at how to improve that one, but it's a bit difficult to see what you want to achieve there. Are you trying to debounce it by 1000ms and only emit once?
To prevent the use of double subscriptions in your template, you can also change your template a bit:
<ng-container *ngIf="definitions | async as defs">
<div class="format-options" *ngIf="defs.length > 0">
<div *ngFor="let definition of defs" class="search-result">
<div>{{definition.name}}</div>
<div>{{definition.description}}</div>
</div>
</div>
</ng-container>
Upvotes: 5