Angular Content Projection and debounce time
import { Component , ContentChild , Input , OnDestroy , OnInit , TemplateRef } from '@angular/core' ;
import { EMPTY , Observable , Subject , catchError , debounceTime , map , tap } from 'rxjs' ;
@Component ( {
selector : 'app-dropdown' ,
templateUrl : './dropdown.component.html' ,
styleUrls : [ './dropdown.component.scss' ]
} )
export class DropdownComponent implements OnInit , OnDestroy {
@Input ( 'filter' ) filter : ( query : string ) => Observable < any [ ] > = ( query : string ) => EMPTY ;
@ContentChild ( 'item' , { static : false } ) itemTemplateRef : TemplateRef < any > | null = null ;
queryUpdate = new Subject < Event | null > ( ) ;
$items ?: Observable < any [ ] > ;
isLoading = false ;
ngOnInit ( ) : void {
this . queryUpdate . pipe (
tap ( ( ) => this . isLoading = true ) ,
debounceTime ( 1000 ) ,
map ( event => ( event ?. target as HTMLInputElement ) ?. value ?? '' ) ,
map ( query => this . filter ( query ?? '' )
. pipe (
catchError ( err => {
console . error ( err )
return [ ]
} )
)
) ,
) . subscribe ( items => {
this . $items = items ;
this . isLoading = false ;
} ) ;
this . queryUpdate . next ( null )
}
ngOnDestroy ( ) : void {
this . queryUpdate . unsubscribe ( )
}
}
< input type ="text " (keyup) ="queryUpdate.next($event) "> {{ isLoading }}
< ul *ngFor ="let item of $items | async ">
< li >
< ng-container
*ngIf ="itemTemplateRef; else elseBlock "
[ngTemplateOutlet] ="itemTemplateRef "
[ngTemplateOutletContext] ="{$implicit:item} ">
</ ng-container >
< ng-template #elseBlock >
{{ item }}
</ ng-template >
</ li >
</ ul >
import { Component } from '@angular/core' ;
import { Observable , filter , map , mergeMap , of , tap } from 'rxjs' ;
@Component ( {
selector : 'app-root' ,
templateUrl : './app.component.html' ,
styleUrls : [ './app.component.scss' ]
} )
export class AppComponent {
title = 'content-projection-experiment' ;
public fetchItems ( query : string ) : Observable < string [ ] > {
console . log ( "AppComponent" , query ) ;
return of ( [ "Foxtrot" , "Uniform" , "Charlie" , "Kilo" ] )
. pipe (
map ( items => items
. filter (
item => item . toLowerCase ( ) . indexOf ( query . toLowerCase ( ) ) >= 0
)
) ,
tap ( item => { if ( Math . floor ( Math . random ( ) * 1000 ) % 2 == 0 ) throw new Error ( "AAAAAAAAAA" ) } )
)
}
}
< router-outlet > </ router-outlet >
< app-dropdown
[filter] ="fetchItems ">
< ng-template #item let-item >
{{ item | uppercase }}
</ ng-template >
</ app-dropdown >