File

projects/core/src/cms/facade/page-meta.service.ts

Description

Service that collects the page meta data by using injected page resolvers.

Index

Properties
Methods

Constructor

constructor(cms: CmsService, unifiedInjector: UnifiedInjector, pageMetaConfig: PageMetaConfig, platformId: string)
Parameters :
Name Type Optional
cms CmsService No
unifiedInjector UnifiedInjector No
pageMetaConfig PageMetaConfig No
platformId string No

Methods

getMeta
getMeta()

Returns the observed page meta data for the current page.

The data is resolved by various PageResolvers, which are configurable.

Returns : Observable<PageMeta | null>
Protected getMetaResolver
getMetaResolver(page: Page)

Return the resolver with the best match, based on a score generated by the resolver.

Resolvers match by default on PageType and page.template.

Parameters :
Name Type Optional
page Page No
Protected getResolverMethods
getResolverMethods()

Returns an object with resolvers. The object properties represent the PageMeta property, i.e.:

{
  title: 'resolveTitle',
  robots: 'resolveRobots'
}

This list of resolvers is filtered for CSR vs SSR processing since not all resolvers are relevant during browsing.

Returns : literal type
Protected resolve
resolve(metaResolver: PageMetaResolver)

If a PageResolver has implemented a resolver interface, the resolved data is merged into the PageMeta object.

Parameters :
Name Type Optional
metaResolver PageMetaResolver No
Returns : Observable<PageMeta>

Properties

Protected meta$
Type : Observable<PageMeta | null>
Default value : defer(() => this.cms.getCurrentPage() ).pipe( filter((page) => Boolean(page)), switchMap((page: Page) => this.getMetaResolver(page)), switchMap((metaResolver: PageMetaResolver | undefined) => metaResolver ? this.resolve(metaResolver) : of(null) ), shareReplay({ bufferSize: 1, refCount: true }) )
Protected resolvers$
Type : Observable<PageMetaResolver[]>
Default value : this.unifiedInjector .getMulti(PageMetaResolver) .pipe(shareReplay({ bufferSize: 1, refCount: true })) as Observable< PageMetaResolver[] >
import { isPlatformBrowser } from '@angular/common';
import { Inject, Injectable, isDevMode, PLATFORM_ID } from '@angular/core';
import { defer, Observable, of } from 'rxjs';
import { filter, map, shareReplay, switchMap } from 'rxjs/operators';
import { UnifiedInjector } from '../../lazy-loading/unified-injector';
import { resolveApplicable } from '../../util/applicable';
import { uniteLatest } from '../../util/rxjs/unite-latest';
import { Page, PageMeta } from '../model/page.model';
import { PageMetaConfig } from '../page/config/page-meta.config';
import { PageMetaResolver } from '../page/page-meta.resolver';
import { CmsService } from './cms.service';

/**
 * Service that collects the page meta data by using injected page resolvers.
 */
@Injectable({
  providedIn: 'root',
})
export class PageMetaService {
  constructor(
    protected cms: CmsService,
    protected unifiedInjector: UnifiedInjector,
    protected pageMetaConfig: PageMetaConfig,
    @Inject(PLATFORM_ID) protected platformId: string
  ) {}

  protected resolvers$: Observable<PageMetaResolver[]> = this.unifiedInjector
    .getMulti(PageMetaResolver)
    .pipe(shareReplay({ bufferSize: 1, refCount: true })) as Observable<
    PageMetaResolver[]
  >;

  protected meta$: Observable<PageMeta | null> = defer(() =>
    this.cms.getCurrentPage()
  ).pipe(
    filter((page) => Boolean(page)),
    switchMap((page: Page) => this.getMetaResolver(page)),
    switchMap((metaResolver: PageMetaResolver | undefined) =>
      metaResolver ? this.resolve(metaResolver) : of(null)
    ),
    shareReplay({ bufferSize: 1, refCount: true })
  );

  /**
   * Returns the observed page meta data for the current page.
   *
   * The data is resolved by various PageResolvers, which are configurable.
   */
  getMeta(): Observable<PageMeta | null> {
    return this.meta$;
  }

  /**
   * If a `PageResolver` has implemented a resolver interface, the resolved data
   * is merged into the `PageMeta` object.
   * @param metaResolver
   */
  protected resolve(metaResolver: PageMetaResolver): Observable<PageMeta> {
    const resolverMethods = this.getResolverMethods();
    const resolvedData: Observable<PageMeta>[] = Object.keys(resolverMethods)
      // TODO: Revisit if typing is possible here with Template Literal Types when we update to TS >=4.1
      .filter((key) => (metaResolver as any)[resolverMethods[key]])
      .map((key) => {
        return (metaResolver as any)
          [resolverMethods[key]]()
          .pipe(map((data) => ({ [key]: data })));
      });

    if (resolvedData.length === 0) {
      // uniteLatest will fail otherwise
      return of({});
    } else {
      return uniteLatest(resolvedData).pipe(
        map((data) => Object.assign({}, ...data))
      );
    }
  }

  /**
   * Returns an object with resolvers. The object properties represent the `PageMeta` property, i.e.:
   *
   * ```
   * {
   *   title: 'resolveTitle',
   *   robots: 'resolveRobots'
   * }
   * ```
   *
   * This list of resolvers is filtered for CSR vs SSR processing since not all resolvers are
   * relevant during browsing.
   */
  protected getResolverMethods(): { [property: string]: string } {
    let resolverMethods: Record<string, string> = {};
    // filter the resolvers to avoid unnecessary processing in CSR
    this.pageMetaConfig?.pageMeta?.resolvers
      ?.filter((resolver) => {
        return (
          // always resolve in SSR
          !isPlatformBrowser(this.platformId ?? '') ||
          // resolve in CSR when it's not disabled
          !resolver.disabledInCsr ||
          // resolve in CSR when resolver is enabled in devMode
          (isDevMode() && this.pageMetaConfig?.pageMeta?.enableInDevMode)
        );
      })
      .forEach(
        (resolver) => (resolverMethods[resolver.property] = resolver.method)
      );
    return resolverMethods;
  }

  /**
   * Return the resolver with the best match, based on a score
   * generated by the resolver.
   *
   * Resolvers match by default on `PageType` and `page.template`.
   */
  protected getMetaResolver(
    page: Page
  ): Observable<PageMetaResolver | undefined> {
    return this.resolvers$.pipe(
      map((resolvers) => resolveApplicable(resolvers, [page], [page]))
    );
  }
}

result-matching ""

    No results matching ""