A friend on StackOverflow had a problem:

In feature Module, lazy-load a Component:

Notice that in line 9 of the figure above, CommonModule is imported.

The Component template file that is lazily loaded uses the Async pipe, which is now in CommonModule. However, because this module is lazily loaded, it is not statically defined in the feature Module declarations. Therefore, when the Component is loaded, the context cannot access the Async Pipe.

So, you end up with a runtime error:

ERROR Error: The pipe ‘async’ could not be found!

The solution, I have already replied in StackOverflow:

https://stackoverflow.com/que…

In the Component to be lazily loaded, manually add the definition of the feature Module to which the Component belongs:

import { ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
import { Product, provideDefaultConfig, CmsConfig } from '@spartacus/core';
import { CurrentProductService, MediaModule, OutletModule, CarouselModule } from '@spartacus/storefront';
import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
import { distinctUntilChanged, filter, map, tap } from 'rxjs/operators';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';

@Component({
  selector: 'app-product-images',
  templateUrl: './razer-product-images.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomProductImagesComponent {
  private mainMediaContainer = new BehaviorSubject(null);

  private product$: Observable<Product> = this.currentProductService
    .getProduct()
    .pipe(
      filter(Boolean),
      distinctUntilChanged(),
      tap((p: Product) => {
        this.mainMediaContainer.next(p.images?.PRIMARY ? p.images.PRIMARY : {});
      })
    );

  thumbs$: Observable<any[]> = this.product$.pipe(
    map((p: Product) => this.createThumbs(p))
  );

  mainImage$ = combineLatest([this.product$, this.mainMediaContainer]).pipe(
    map(([, container]) => container)
  );

  constructor(private currentProductService: CurrentProductService) {}

  openImage(item: any): void {
    this.mainMediaContainer.next(item);
  }

  isActive(thumbnail): Observable<boolean> {
    return this.mainMediaContainer.pipe(
      filter(Boolean),
      map((container: any) => {
        return (
          container.zoom &&
          container.zoom.url &&
          thumbnail.zoom &&
          thumbnail.zoom.url &&
          container.zoom.url === thumbnail.zoom.url
        );
      })
    );
  }

  /** find the index of the main media in the list of media */
  getActive(thumbs: any[]): Observable<number> {
    return this.mainMediaContainer.pipe(
      filter(Boolean),
      map((container: any) => {
        const current = thumbs.find(
          (t) =>
            t.media &&
            container.zoom &&
            t.media.container &&
            t.media.container.zoom &&
            t.media.container.zoom.url === container.zoom.url
        );
        return thumbs.indexOf(current);
      })
    );
  }

  /**
   * Return an array of CarouselItems for the product thumbnails.
   * In case there are less then 2 thumbs, we return null.
   */
  private createThumbs(product: Product): Observable<any>[] {
    if (
      !product.images ||
      !product.images.GALLERY ||
      product.images.GALLERY.length < 2
    ) {
      return [];
    }

    return (<any[]>product.images.GALLERY).map((c) => of({ container: c }));
  }
}

@NgModule({
  imports: [
    CommonModule,
    RouterModule,
    MediaModule,
    OutletModule,
    CarouselModule
  ],
  declarations:[CustomProductImagesComponent]
})
export class CustomProductImagesModule {}

More of Jerry’s original articles can be found in “Wang Zixi “: