From a4f1421b7b0133e55be6e9aaf508508dabb8cc99 Mon Sep 17 00:00:00 2001 From: Adithya Sreyaj Date: Thu, 21 Sep 2023 12:27:21 +0530 Subject: [PATCH] feat(load-async): add typing --- .../src/load-async/load-async.directive.ts | 20 ++++++++----- .../src/load-async/load-async.service.ts | 30 +++++++++---------- .../wrapper/load-async-wrapper.component.ts | 16 +++++----- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/projects/components/src/load-async/load-async.directive.ts b/projects/components/src/load-async/load-async.directive.ts index 14fde9987..093c98f5b 100644 --- a/projects/components/src/load-async/load-async.directive.ts +++ b/projects/components/src/load-async/load-async.directive.ts @@ -19,20 +19,20 @@ import { @Directive({ selector: '[htLoadAsync]' }) -export class LoadAsyncDirective implements OnChanges, OnDestroy { +export class LoadAsyncDirective implements OnChanges, OnDestroy { @Input('htLoadAsync') - public data$?: Observable; + public data$?: Observable; @Input('htLoadAsyncConfig') public config?: LoadAsyncConfig; - private readonly wrapperParamsSubject: ReplaySubject = new ReplaySubject(1); + private readonly wrapperParamsSubject: ReplaySubject> = new ReplaySubject(1); private readonly wrapperInjector: Injector; - private wrapperView?: ComponentRef; + private wrapperView?: ComponentRef>; public constructor( private readonly viewContainer: ViewContainerRef, private readonly loadAsyncService: LoadAsyncService, - public readonly templateRef: TemplateRef + public readonly templateRef: TemplateRef> ) { this.wrapperInjector = Injector.create({ providers: [ @@ -49,7 +49,7 @@ export class LoadAsyncDirective implements OnChanges, OnDestroy { if (this.data$) { this.wrapperView = this.wrapperView || this.buildWrapperView(); this.wrapperParamsSubject.next({ - state$: this.loadAsyncService.mapObservableState(this.data$), + state$: this.loadAsyncService.mapObservableState(this.data$), content: this.templateRef, config: this.config }); @@ -64,8 +64,12 @@ export class LoadAsyncDirective implements OnChanges, OnDestroy { this.wrapperParamsSubject.complete(); } - private buildWrapperView(): ComponentRef { - return this.viewContainer.createComponent(LoadAsyncWrapperComponent, { + public static ngTemplateContextGuard(_dir: LoadAsyncDirective, _ctx: unknown): _ctx is LoadAsyncContext { + return true; + } + + private buildWrapperView(): ComponentRef> { + return this.viewContainer.createComponent>(LoadAsyncWrapperComponent, { injector: this.wrapperInjector }); } diff --git a/projects/components/src/load-async/load-async.service.ts b/projects/components/src/load-async/load-async.service.ts index 701095539..e4a7222a6 100644 --- a/projects/components/src/load-async/load-async.service.ts +++ b/projects/components/src/load-async/load-async.service.ts @@ -7,18 +7,18 @@ import { LoadAsyncStateType } from './load-async-state.type'; @Injectable({ providedIn: 'root' }) export class LoadAsyncService { - public mapObservableState(data$: Observable): Observable { + public mapObservableState(data$: Observable): Observable> { return data$.pipe( - map(data => this.buildStateForEmittedData(data)), - defaultIfEmpty(this.buildNoDataState()), - catchError(error => of(this.buildStateForEmittedError(error))), + map(data => this.buildStateForEmittedData(data)), + defaultIfEmpty(this.buildNoDataState()), + catchError(error => of(this.buildStateForEmittedError(error))), startWith({ type: LoadAsyncStateType.Loading }) ); } - private buildStateForEmittedData(data: unknown): AsyncState { + private buildStateForEmittedData(data: T): AsyncState { if (Array.isArray(data) && data.length === 0) { - return this.buildNoDataState(); + return this.buildNoDataState(); } return { @@ -30,8 +30,8 @@ export class LoadAsyncService { }; } - private buildStateForEmittedError(error: Error): AsyncState { - const asyncState: AsyncState = { type: LoadAsyncStateType.GenericError }; + private buildStateForEmittedError(error: Error): AsyncState { + const asyncState: AsyncState = { type: LoadAsyncStateType.GenericError }; if (error instanceof CustomError) { asyncState.description = error.message; } @@ -39,16 +39,16 @@ export class LoadAsyncService { return asyncState; } - private buildNoDataState(): AsyncState { + private buildNoDataState(): AsyncState { return { type: LoadAsyncStateType.NoData }; } } -export interface LoadAsyncContext { - htLoadAsync: unknown; - $implicit: unknown; +export interface LoadAsyncContext { + htLoadAsync: T; + $implicit: T; } export interface LoadAsyncConfig { @@ -57,7 +57,7 @@ export interface LoadAsyncConfig { error?: NoDataOrErrorStateConfig; } -export type AsyncState = LoadingAsyncState | SuccessAsyncState | NoDataOrErrorAsyncState; +export type AsyncState = LoadingAsyncState | SuccessAsyncState | NoDataOrErrorAsyncState; export const enum LoaderType { Spinner = 'spinner', @@ -75,9 +75,9 @@ export const enum LoaderType { interface LoadingAsyncState { type: LoadAsyncStateType.Loading; } -interface SuccessAsyncState { +interface SuccessAsyncState { type: LoadAsyncStateType.Success; - context: LoadAsyncContext; + context: LoadAsyncContext; } export interface NoDataOrErrorAsyncState { diff --git a/projects/components/src/load-async/wrapper/load-async-wrapper.component.ts b/projects/components/src/load-async/wrapper/load-async-wrapper.component.ts index dc98eb1b0..f72716226 100644 --- a/projects/components/src/load-async/wrapper/load-async-wrapper.component.ts +++ b/projects/components/src/load-async/wrapper/load-async-wrapper.component.ts @@ -16,7 +16,7 @@ import { NoDataOrErrorStateConfigWithCustomTemplate } from '../load-async.service'; -export const ASYNC_WRAPPER_PARAMETERS$ = new InjectionToken>( +export const ASYNC_WRAPPER_PARAMETERS$ = new InjectionToken>>( 'ASYNC_WRAPPER_PARAMETERS$' ); @@ -59,8 +59,8 @@ export const ASYNC_WRAPPER_PARAMETERS$ = new InjectionToken; +export class LoadAsyncWrapperComponent { + public readonly state$: Observable>; public icon?: IconType; public loaderType?: LoaderType; @@ -71,7 +71,7 @@ export class LoadAsyncWrapperComponent { public config?: LoadAsyncConfig; public customNoDataOrErrorTemplate?: TemplateRef; - public constructor(@Inject(ASYNC_WRAPPER_PARAMETERS$) parameters$: Observable) { + public constructor(@Inject(ASYNC_WRAPPER_PARAMETERS$) parameters$: Observable>) { this.state$ = parameters$.pipe( tap(params => { this.content = params.content; @@ -82,7 +82,7 @@ export class LoadAsyncWrapperComponent { ); } - private updateMessage(state: AsyncState): void { + private updateMessage(state: AsyncState): void { switch (state.type) { case LoadAsyncStateType.Loading: this.loaderType = this.config?.load?.loaderType; @@ -146,8 +146,8 @@ export class LoadAsyncWrapperComponent { } } -export interface LoadAsyncWrapperParameters { - state$: Observable; - content: TemplateRef; +export interface LoadAsyncWrapperParameters { + state$: Observable>; + content: TemplateRef>; config?: LoadAsyncConfig; }