All files / src/lib/engine NgxPageDirective.ts

35% Statements 21/60
10.34% Branches 3/29
28.57% Functions 2/7
39.58% Lines 19/48

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222              7x 7x 7x 7x     7x 7x   7x   7x                         7x                                     1x                                           1x                         1x                     1x                         1x                                                             1x 1x                                                             1x                                 1x 1x                                                              
/**
 * @module lib/engine/NgxPageDirective
 * @description Base page component for Decaf Angular applications.
 * @summary Provides a page-level base class (NgxPageDirective) that extends NgxComponentDirective and
 * offers page-focused utilities such as menu management, title handling and router event hooks.
 * @link {@link NgxPageDirective}
 */
import { AfterViewInit, Directive, Inject, inject } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { NavigationEnd, NavigationStart } from '@angular/router';
import { MenuController } from '@ionic/angular';
import { Model } from '@decaf-ts/decorator-validation';
import { IMenuItem } from './interfaces';
import { CPTKN } from './constants';
import { removeFocusTrap } from '../utils/helpers';
import { KeyValue } from './types';
import { NgxComponentDirective } from './NgxComponentDirective';
 
import { shareReplay, takeUntil } from 'rxjs';
 
/**
 * @description Base directive for page-level components in Decaf Angular applications.
 * @summary Abstract directive that provides foundational functionality for page components.
 * Extends NgxComponentDirective to add page-specific features including menu management,
 * page title handling, and Ionic lifecycle hooks. This directive serves as the base class for
 * all page-level components, providing consistent behavior for navigation, routing, and UI state.
 * @class NgxPageDirective
 * @extends {NgxComponentDirective}
 * @memberOf module:lib/engine/NgxPageDirective
 */
@Directive()
export abstract class NgxPageDirective extends NgxComponentDirective implements AfterViewInit {
  /**
   * @description Application name for display or identification purposes.
   * @summary Stores the name of the application, which can be used for display in headers,
   * titles, or for logging and identification throughout the app.
   * @type {string}
   * @memberOf module:lib/engine/NgxPageDirective
   */
  appName?: string;
 
  /**
   * @description Page title text for the current view.
   * @summary Stores the title text to be displayed for this page. This can be set dynamically
   * based on the current route or menu configuration and is used to update the browser's
   * title bar or page header.
   * @type {string}
   * @default ''
   * @memberOf module:lib/engine/NgxPageDirective
   */
  title: string = '';
 
  /**
   * @description Global key-value pairs for application-wide settings.
   * @summary This property stores global configuration values that can be accessed
   * throughout the application. It is typically used to manage shared state or
   * settings that are relevant across multiple components or services.
   * @type {KeyValue}
   * @memberOf module:lib/engine/NgxPageDirective
   */
  globals!: KeyValue;
 
  /**
   * @description Ionic menu controller service for menu management.
   * @summary Injected service that provides programmatic control over Ionic menu components.
   * This service allows the component to open, close, toggle, and manage menu states within
   * the application. It provides access to menu functionality for implementing navigation
   * and layout features that require menu interaction.
   * @protected
   * @type {MenuController}
   * @memberOf module:lib/engine/NgxPageDirective
   */
  protected menuController: MenuController = inject(MenuController);
 
  /**
   * @description Menu items array for page navigation.
   * @summary Contains the collection of menu items available for this page. Each menu item
   * defines a navigation option with properties like label, URL, icon, and visibility settings.
   * This array is used to construct the application's navigation menu and can be filtered or
   * customized per page.
   * @protected
   * @type {IMenuItem[]}
   * @default []
   * @memberOf module:lib/engine/NgxPageDirective
   */
  protected menu: IMenuItem[] = [];
 
  /**
   * @description Angular Title service for browser title management.
   * @summary Injected service that provides control over the browser's document title.
   * Used to dynamically set the page title based on the current route or active menu item,
   * improving SEO and user experience.
   * @protected
   * @type {Title}
   * @memberOf module:lib/engine/NgxPageDirective
   */
  protected titleService: Title = inject(Title);
 
  /**
   * @description Flag indicating whether the page should display the navigation menu.
   * @summary Controls the visibility and availability of the application menu for this page.
   * When set to true, the menu is enabled and accessible to users. When false, the menu
   * is disabled, which is useful for pages like login screens or standalone views that
   * should not show navigation options.
   * @protected
   * @type {boolean}
   * @default true
   * @memberOf module:lib/engine/NgxPageDirective
   */
  protected hasMenu: boolean = true;
 
  /**
   * @description Currently active route path for the page (without leading slash).
   * @summary This protected property stores the current route segment used by the
   * page to determine the displayed title and menu state. It is updated on
   * NavigationEnd events (see ngAfterViewInit) and consumed by the `pageTitle`
   * getter and `setPageTitle()` method. When undefined the component will
   * attempt to resolve the route from the router URL.
   * @protected
   * @type {string | undefined}
   * @default undefined
   */
  protected currentRoute?: string;
 
  /**
   * @description Constructor for NgxPageDirective.
   * @summary Initializes the page directive with optional locale root and menu visibility settings.
   * Calls the parent NgxComponentDirective constructor to set up base functionality including
   * logging, localization, and component identification.
   * @param {string} [localeRoot] - Optional locale root key for internationalization
   * @param {boolean} [hasMenu=true] - Whether this page should display the menu
   * @memberOf module:lib/engine/NgxPageDirective
   */
 
  constructor(
    // eslint-disable-next-line @angular-eslint/prefer-inject
    @Inject(CPTKN) localeRoot: string = 'NgxPageDirective',
    // eslint-disable-next-line @angular-eslint/prefer-inject
    @Inject(CPTKN) hasMenu: boolean = true,
  ) {
    super(localeRoot);
    this.hasMenu = hasMenu;
    // subscribe to media service color scheme changes
  }
 
  get pageTitle(): string {
    Iif (this.title) return this.title;
    Iif (this.locale) return `${this.locale}.title`;
    Iif (this.currentRoute) return `${this.currentRoute}.title`;
    return `${(this.model as Model).constructor.name}.title`;
  }
 
  // async ngOnInit(): Promise<void> {}
 
  override async initialize(): Promise<void> {
    await super.initialize();
    // connect component to media service for color scheme toggling
    this.currentRoute = this.router.url.replace('/', '');
    Iif (!this.route) this.route = this.currentRoute;
    this.setPageTitle(this.currentRoute);
  }
 
  /**
   * @description Ionic lifecycle hook called when the page is about to enter view.
   * @summary This lifecycle hook is triggered just before the page becomes visible to the user.
   * It enables or disables the application menu based on the hasMenu property, allowing pages
   * to control whether the menu should be accessible. This is useful for pages like login screens
   * where the menu should be hidden.
   * @return {Promise<void>} A promise that resolves when menu state is updated
   * @memberOf module:lib/engine/NgxPageDirective
   */
  async ngAfterViewInit(): Promise<void> {
    this.router.events
      .pipe(takeUntil(this.destroySubscriptions$), shareReplay(1))
      .subscribe(async (event) => {
        Iif (event instanceof NavigationEnd) {
          const url = (event?.url || '').replace('/', '');
          this.currentRoute = url;
          Iif (this.hasMenu) this.hasMenu = url !== 'login' && url !== '';
          this.title = this.pageTitle;
          await this.setPageTitle(url);
          this.changeDetectorRef.detectChanges();
        }
        Iif (event instanceof NavigationStart) {
          const url = (event?.url || '').replace('/', '');
          Iif (this.hasMenu) this.hasMenu = url !== 'login' && url !== '';
          removeFocusTrap();
        }
      });
    if (!this.route) this.route = this.router.url.replace('/', '');
    await this.menuController.enable(this.hasMenu);
  }
 
  /**
   * @description Sets the browser page title based on the current route.
   * @summary Updates the browser's document title by finding the active menu item that matches
   * the provided route. If a matching menu item is found, it sets the title using the format
   * "Decaf For Angular - {menu title or label}". This improves SEO and provides clear context
   * to users about the current page. If a custom menu array is provided, it uses that instead
   * of the component's default menu.
   * @protected
   * @param {string} route - The current route path to match against menu items
   * @param {IMenuItem[]} [menu] - Optional custom menu array to search (uses this.menu if not provided)
   * @return {void}
   * @memberOf module:lib/engine/NgxPageDirective
   */
  protected async setPageTitle(route?: string, menu?: IMenuItem[]): Promise<void> {
    Iif (!route) route = this.router.url.replace('/', '');
    Iif (!menu) menu = this.menu;
    const activeMenu = menu.find((item) => item?.url?.includes(route));
    Iif (activeMenu) {
      let label = `${(activeMenu?.title || activeMenu?.label || '').toLowerCase()}`;
      Iif (!label.startsWith('menu.')) {
        label = `menu.${label}`;
      }
      const title = `${await this.translate(label)} ${this.appName ? `- ${this.appName}` : ''}`;
      this.titleService.setTitle(title);
      Iif (!this.title) this.title = title;
    }
  }
}