File

projects/storefrontlib/cms-structure/page/slot/page-slot.component.ts

Description

The PageSlotComponent is used to render the CMS page slot and it's components.

The Page slot host element will be supplemented with css classes so that the layout can be fully controlled by customers:

  • The page slot position is added as a css class by default.
  • The cx-pending is added for as long as the slot hasn't start loading.
  • The page-fold style class is added for the page slot which is configured as the page fold.

Implements

OnInit OnDestroy

Metadata

changeDetection ChangeDetectionStrategy.OnPush
selector cx-page-slot,[cx-page-slot]
templateUrl ./page-slot.component.html

Index

Properties
Methods
Inputs
HostBindings
Accessors

Constructor

constructor(cmsService: CmsService, dynamicAttributeService: DynamicAttributeService, renderer: Renderer2, elementRef: ElementRef, cd: ChangeDetectorRef, pageSlotService: PageSlotService)
Parameters :
Name Type Optional
cmsService CmsService No
dynamicAttributeService DynamicAttributeService No
renderer Renderer2 No
elementRef ElementRef No
cd ChangeDetectorRef No
pageSlotService PageSlotService No

Inputs

class
Type : string

Maintains css classes introduced by the host and adds additional classes.

hasComponents
Type : boolean
Default value : false

Indicates that the page slot doesn't contain any components. This is no longer used in spartacus, but kept for backwards compatibility.

isPageFold
Type : boolean
Default value : false

Indicates that the page slot is the last page slot above the fold.

position
Type : string

The position represents the unique key for a page slot on a single page, but can be reused cross pages.

The position is used to find the CMS components for the page slot. It is also added as an additional CSS class so that layout can be applied.

HostBindings

class.cx-pending
Type : boolean
Default value : true

Indicates that the components of the page slot haven't been loaded as long as the isPending state is true.

Methods

Protected decorate
decorate(slot: ContentSlotData)
Parameters :
Name Type Optional
slot ContentSlotData No
Returns : void
getComponentDeferOptions
getComponentDeferOptions(componentType: string)

The DeferLoadingStrategy indicates whether the component should be rendered instantly or whether it should be deferred.

Parameters :
Name Type Optional
componentType string No
Protected isDistinct
isDistinct(old: ContentSlotData, current: ContentSlotData)
Parameters :
Name Type Optional
old ContentSlotData No
current ContentSlotData No
Returns : any
isLoaded
isLoaded(loadState: boolean)
Parameters :
Name Type Optional
loadState boolean No
Returns : void
ngOnDestroy
ngOnDestroy()
Returns : void
ngOnInit
ngOnInit()
Returns : void

Properties

class
Type : string
Decorators :
@Input()
@HostBinding()

Maintains css classes introduced by the host and adds additional classes.

components
Type : ContentSlotComponentData[]
components$
Type : Observable<ContentSlotComponentData[]>
Default value : this.slot$.pipe( map((slot) => slot?.components ?? []) )

Observes the components for the given page slot.

hasComponents
Default value : false
Decorators :
@HostBinding('class.has-components')
@Input()

Indicates that the page slot doesn't contain any components. This is no longer used in spartacus, but kept for backwards compatibility.

isPageFold
Default value : false
Decorators :
@HostBinding('class.page-fold')
@Input()

Indicates that the page slot is the last page slot above the fold.

isPending
Default value : true
Decorators :
@HostBinding('class.cx-pending')

Indicates that the components of the page slot haven't been loaded as long as the isPending state is true.

Private lastPosition
Type : string

Tracks the last used position, in case the page slot is used dynamically

Private pendingComponentCount
Type : number
Default value : 0

Keeps track of the pending components that must be loaded for the page slot

Protected position$
Type : BehaviorSubject<string>
Default value : new BehaviorSubject(undefined)
Protected slot$
Type : Observable<ContentSlotData>
Default value : this.position$.pipe( switchMap((position) => this.cmsService.getContentSlot(position)), distinctUntilChanged(this.isDistinct) )
Protected subscription
Type : Subscription
Default value : new Subscription()

Accessors

position
getposition()
setposition(value: string)

The position represents the unique key for a page slot on a single page, but can be reused cross pages.

The position is used to find the CMS components for the page slot. It is also added as an additional CSS class so that layout can be applied.

Parameters :
Name Type Optional
value string No
Returns : void
pending
getpending()
setpending(count: number)

Sets the pending count for the page slot components. Once all pending components are loaded, the isPending flag is updated, so that the associated class can be updated

Parameters :
Name Type Optional
count number No
Returns : void
import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  ElementRef,
  HostBinding,
  Input,
  OnDestroy,
  OnInit,
  Renderer2,
} from '@angular/core';
import {
  CmsService,
  ContentSlotComponentData,
  ContentSlotData,
  DynamicAttributeService,
} from '@spartacus/core';
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators';
import { IntersectionOptions } from '../../../layout/loading/intersection.model';
import { PageSlotService } from './page-slot.service';

/**
 * The `PageSlotComponent` is used to render the CMS page slot and it's components.
 *
 * The Page slot host element will be supplemented with css classes so that the layout
 * can be fully controlled by customers:
 * - The page slot _position_ is added as a css class by default.
 * - The `cx-pending` is added for as long as the slot hasn't start loading.
 * - The `page-fold` style class is added for the page slot which is configured as the page fold.
 */
@Component({
  selector: 'cx-page-slot,[cx-page-slot]',
  templateUrl: './page-slot.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PageSlotComponent implements OnInit, OnDestroy {
  /**
   * The position represents the unique key for a page slot on a single page, but can
   * be reused cross pages.
   *
   * The position is used to find the CMS components for the page slot. It is also
   * added as an additional CSS class so that layout can be applied.
   */
  @HostBinding('attr.position')
  @Input()
  set position(value: string) {
    this.position$.next(value);
  }
  get position(): string {
    return this.position$.value;
  }

  /**
   * Maintains css classes introduced by the host and adds additional classes.
   */
  @Input() @HostBinding() class: string;

  /**
   * Indicates that the page slot is the last page slot above the fold.
   */
  @HostBinding('class.page-fold') @Input() isPageFold = false;

  /**
   * Indicates that the components of the page slot haven't been loaded as long
   * as the isPending state is true.
   */
  @HostBinding('class.cx-pending') isPending = true;

  /**
   * Indicates that the page slot doesn't contain any components. This is no
   * longer used in spartacus, but kept for backwards compatibility.
   */
  @HostBinding('class.has-components') @Input() hasComponents = false;

  protected position$: BehaviorSubject<string> = new BehaviorSubject(undefined);

  components: ContentSlotComponentData[];

  protected slot$: Observable<ContentSlotData> = this.position$.pipe(
    switchMap((position) => this.cmsService.getContentSlot(position)),
    distinctUntilChanged(this.isDistinct)
  );

  /** Observes the components for the given page slot. */
  components$: Observable<ContentSlotComponentData[]> = this.slot$.pipe(
    map((slot) => slot?.components ?? [])
  );

  protected subscription: Subscription = new Subscription();

  /** Keeps track of the pending components that must be loaded for the page slot */
  private pendingComponentCount = 0;

  /** Tracks the last used position, in case the page slot is used dynamically */
  private lastPosition: string;
  constructor(
    protected cmsService: CmsService,
    protected dynamicAttributeService: DynamicAttributeService,
    protected renderer: Renderer2,
    protected elementRef: ElementRef,
    protected cd: ChangeDetectorRef,
    protected pageSlotService: PageSlotService
  ) {}

  ngOnInit() {
    this.subscription.add(
      this.slot$.pipe(tap((slot) => this.decorate(slot))).subscribe((value) => {
        this.components = value?.components || [];
        this.cd.markForCheck();
      })
    );
  }

  protected decorate(slot: ContentSlotData): void {
    let cls = this.class || '';

    if (this.lastPosition && cls.indexOf(this.lastPosition) > -1) {
      cls = cls.replace(this.lastPosition, '');
    }
    if (this.position$.value) {
      cls += ` ${this.position$.value}`;
      this.lastPosition = this.position$.value;
    }

    // host bindings
    this.pending = slot?.components?.length || 0;
    this.hasComponents = slot?.components?.length > 0;
    if (cls && cls !== this.class) {
      this.class = cls;
    }

    if (slot) {
      this.dynamicAttributeService.addAttributesToSlot(
        this.elementRef.nativeElement,
        this.renderer,
        slot
      );
    }
  }

  /**
   * Sets the pending count for the page slot components. Once all pending components are
   * loaded, the `isPending` flag is updated, so that the associated class can be updated
   */
  protected set pending(count: number) {
    this.pendingComponentCount = count;
    this.isPending = this.pendingComponentCount > 0;
  }

  protected get pending(): number {
    return this.pendingComponentCount;
  }

  /*
   * Is triggered when a component is added to the view. This is used to
   * update the pending count
   */
  isLoaded(loadState: boolean) {
    if (loadState) {
      this.pending--;
      this.cd.markForCheck();
    }
  }

  /**
   * The `DeferLoadingStrategy` indicates whether the component should be
   * rendered instantly or whether it should be deferred.
   */
  getComponentDeferOptions(componentType: string): IntersectionOptions {
    return this.pageSlotService.getComponentDeferOptions(
      this.position,
      componentType
    );
  }

  protected isDistinct(old: ContentSlotData, current: ContentSlotData) {
    return (
      current.components &&
      old.components?.length === current.components.length &&
      !old.components.find(
        (el, index) => el.uid !== current.components[index].uid
      )
    );
  }

  ngOnDestroy() {
    this.subscription?.unsubscribe();
  }
}
<ng-template
  [cxOutlet]="position"
  [cxOutletContext]="{ components$: components$ }"
>
  <ng-template
    *ngFor="let component of components"
    [cxOutlet]="component.flexType"
    [cxOutletContext]="{ component: component }"
    [cxOutletDefer]="getComponentDeferOptions(component.flexType)"
    (loaded)="isLoaded($event)"
  >
    <ng-container [cxComponentWrapper]="component"></ng-container>
  </ng-template>
</ng-template>
Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""