I create a lazyLoad Directive to handle lazy load component.
@Directive({
selector: '[lazyComponent]',
})
export class LazyCompDirective<I, T extends LazyLoadedComponent<I>> implements OnInit, OnDestroy {
private _inputs: I|null = null;
private compRef: ComponentRef<T>|null = null;
private readonly subscription = new Subscription();
private type?: Type<T>;
@Input('lazyComponent') public set component(type: Type<T>) {
if (type) {
this.compRef = this.viewContainerRef.createComponent(type);
this.refreshInputs(this._inputs);
this.type = type;
}
}
@Input() public set inputs(data: I) {
if (this.compRef) {
this.refreshInputs(data);
(this.compRef).hostView.detectChanges();
} else {
this._inputs = data;
}
}
public constructor(private readonly viewContainerRef: ViewContainerRef) {}
ngOnInit(): void {
if (this.type) {
this.compRef = this.viewContainerRef.createComponent(this.type);
this.refreshInputs(this._inputs);
}
}
public ngOnDestroy() {
this.compRef = null;
this.subscription.unsubscribe();
}
private refreshInputs(inputs: I | null) {
if (inputs) {
Object.keys((inputs)).forEach(inputName => {
// @ts-expect-error: skip error
(this.compRef as ComponentRef<unknown>).instance[inputName] = inputs[inputName];
});
}
}
static ngTemplateContextGuard<T extends LazyLoadedComponent<I>, I>(_dir: LazyCompDirective<I, T>, _context: unknown): _context is LazyLoadedComponent<I> {
return true
}
static ngTemplateGuard_inputs<T extends LazyLoadedComponent<I>, I>(_dir: LazyCompDirective<I, T>, _state: I): _state is I {
return true;
}
}
so we can use this property as
<ng-template [lazyComponent]="component" [inputs]="componentInput"></ng-template>
We define this interface so we can always have a strong type with the directive
export interface LazyLoadedComponent<I> {
inputs?: I;
}
to load the component we need to implement this interface
@Component({
selector: 'lazy-component',
template: '<div>I'm Lazy</div>',
})
export class LazyComponent implements LazyLoadedComponent<LazyComponentInput>, OnInit {
public inputs?: LazyComponentInput;
public constructor(
) {}
public ngOnInit(): void {}
}
LazyComponentInput
export interface LazyComponentInput{
a: string,
b: number,
}
I found that the [inputs]
don't really check whether the type is valid or not.
I feel like my type check guard is wrong but I couldn't figure out how should I check the type. Does anyone know how should I fix the type-checking?