Extending the features of the component-level lazy loader thanks to Ivy
Continue from Part 1:
Since the loader uses the same injector-Instance we can inject the same services as the parent Component.
Side-note: The usual lifecycle callbacks are still working on lazy-loaded components.
@Input()
To set the inputs on the loaded component, the loader uses componentInputs: any
as @Input()
private setInputs() {
if (this.componentInstance && this.componentInputs) {
const inputs = Object.keys(this.componentInputs);
for (const inputKey of inputs) {
this.componentInstance[inputKey] = this.componentInputs[inputKey];
}
}
}
setInputs
will be called once after the component is created and on each ngOnChanges
that is called for componentInputs
.
Now you can also set inputs to the loaded component
@Output()
In order to use the outputs of your loaded component, you can just set your callbacks with:
[componentOutputs]="{
outputName: onYourCallbackMethod
}"
Since this object is just a dictionary of key: Function
, its rather "easy" to subscribe to the loaded component outputs.
private unsubForOutputs$ = new Subject();
private setOutputs () {
this.unsubOutputs();
if (this.componentInstance && this.componentOutputs) {
const outputs = Object.keys(this.componentOutputs);
for (const outputKey of outputs) {
if (this.componentInstance[outputKey]) {
const emitter = this.componentInstance[outputKey] as EventEmitter<any>;
emitter.pipe(
takeUntil(this.unsubForOutputs$),
).subscribe(this.componentOutputs[outputKey]);
}
}
}
}
private unsubOutputs () {
this.unsubForOutputs$.next();
}
The prior example had this:
const imported = await DynamicLoaderComponent.LazyComponents[this.component]();
That way the requested component would be loaded (HTTP-call) every time.
To prevent this we can just add a simple cache - object which holds the resolved-promises (in the same way I refactored the registration (again )):
export class DynamicLoaderRegistry {
// Registry
public static LazyComponents: { [key: string]: () => Promise<any> } = {};
// Loaded-Cache
public static AlreadyLoaded: { [key: string]: Promise<any> } = {};
}
// cache the promises
const importComponent = DynamicLoaderRegistry.AlreadyLoaded[this.component]
|| (DynamicLoaderRegistry.AlreadyLoaded[this.component] = DynamicLoaderRegistry.LazyComponents[this.component]());
const imported = await importComponent;
Now if the same component is requested, its only loaded once
See: Current Version
to be continued / tried / tested:
Any thoughts / suggestions / ideas ?