DEV Community

negue
negue

Posted on

2 2

Lazy Loaded Components - #2

Extending the features of the component-level lazy loader thanks to πŸŽ‰ Ivy πŸŽ‰

Continue from Part 1:

1. Inject services into lazy-loaded components:

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.

2. @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 πŸŽ‰

3. @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();
  }

4. Prevent loading the same components multiple times

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 πŸŽ‰

Finalizing the component-loader πŸŽ‰ (.. for now πŸ˜…)

See: Current Version

to be continued / tried / tested:

  • example repo / project
  • show that the component is loading
  • lazy-loaded module (and one of its components)

Any thoughts / suggestions / ideas ?

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Top comments (0)

Cloudinary image

Optimize, customize, deliver, manage and analyze your images.

Remove background in all your web images at the same time, use outpainting to expand images with matching content, remove objects via open-set object detection and fill, recolor, crop, resize... Discover these and hundreds more ways to manage your web images and videos on a scale.

Learn more

πŸ‘‹ Kindness is contagious

Please leave a ❀️ or a friendly comment on this post if you found it helpful!

Okay