Source

app/app.component.ts

import { Component, CUSTOM_ELEMENTS_SCHEMA, inject, OnInit } from '@angular/core';
import { NavigationEnd, NavigationStart, Router, RouterLink, RouterLinkActive } from '@angular/router';
import { IonApp,
  IonSplitPane,
  IonMenu,
  IonContent,
  IonList,
  IonListHeader,
  IonMenuToggle,
  IonItem,
  IonIcon,
  IonLabel,
  IonRouterOutlet,
  IonRouterLink
} from '@ionic/angular/standalone';
import { Title } from '@angular/platform-browser';
import { Platform } from '@ionic/angular';
import { NgxRenderingEngine } from '../lib/engine';
import { ForAngularModule } from 'src/lib/for-angular.module';
import { Model, ModelBuilderFunction } from '@decaf-ts/decorator-validation';
import { addIcons } from 'ionicons';
import * as IonicIcons from 'ionicons/icons';
import { MenuItem } from 'src/app/utils/types';
import { isDevelopmentMode, removeFocusTrap } from 'src/lib/helpers';
import { ForAngularRepository } from './utils/ForAngularRepository';
import { CategoryModel } from './models/CategoryModel';
import { EmployeeModel } from './models/EmployeeModel';
import { DecafRepositoryAdapter } from 'src/lib/components/list/constants';
import { DbAdapterProvider } from './app.config';

try {
  new NgxRenderingEngine();
  Model.setBuilder(Model.fromModel as ModelBuilderFunction);

} catch (e: unknown) {
  throw new Error(`Failed to load rendering engine: ${e}`);
}

/**
 * @description Application title constant
 * @summary This constant holds the main title of the application
 */
const title = "Decaf-ts for Angular";

/**
 * @description Menu items for the application's navigation
 * @summary This constant defines the structure of the application's navigation menu.
 * It includes items for the dashboard, CRUD operations, data lists, and logout.
 * @type {MenuItem[]}
 * @example
 * const menuItem = Menu[0];
 * console.log(menuItem.label); // 'Dashboard'
 * console.log(menuItem.url); // '/dashboard'
 */
const Menu: MenuItem[] = [
  {
    label: 'Dashboard',
    icon: 'apps-outline',
    url: '/dashboard',
  },
  {
    label: 'Crud',
    icon: 'save-outline',
  },
  {
    label: 'Fieldset',
    url: '/fieldset',
  },
  {
    label: 'Read',
    url: '/crud/read',
  },
  {
    label: 'Create / Update',
    url: '/crud/create',
  },
  {
    label: 'Delete',
    url: '/crud/delete',
  },
  {
    label: 'Data Lists',
    icon: 'list-outline',
  },
  {
    label: 'Employees (Infinite)',
    url: '/list/infinite',
  },
  {
    label: 'Categories (Paginated)',
    url: '/list/paginated',
  },
  {
    label: 'Model Lists',
    icon: 'list-outline',
  },
  {
    label: 'Employees (Infinite)',
    url: '/list-model/infinite',
  },
  {
    label: 'Categories (Paginated)',
    url: '/list-model/paginated',
  },
  {
    label: 'Logout',
    title: 'Login',
    icon: 'log-out-outline',
    url: '/login',
    color: 'danger'
  },
];


/**
 * @description Root component of the Decaf-ts for Angular application
 * @summary This component serves as the main entry point for the application.
 * It sets up the navigation menu, handles routing events, and initializes
 * the application state. It also manages the application title and menu visibility.
 * @class
 * @param {Platform} platform - Ionic Platform service
 * @param {Router} router - Angular Router service
 * @param {MenuController} menuController - Ionic MenuController service
 * @param {Title} titleService - Angular Title service
 * @example
 * <app-root></app-root>
 * @mermaid
 * sequenceDiagram
 *   participant App as AppComponent
 *   participant Router
 *   participant MenuController
 *   participant TitleService
 *   participant Repository
 *   App->>App: constructor()
 *   App->>App: ngOnInit()
 *   App->>Router: Subscribe to events
 *   Router-->>App: Navigation events
 *   App->>MenuController: Enable/Disable menu
 *   App->>TitleService: Set page title
 *   App->>App: initializeApp()
 *   alt isDevelopmentMode
 *     App->>Repository: Initialize repositories
 *   end
 */
@Component({
  standalone: true,
  selector: 'app-root',
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  imports: [
    ForAngularModule,
    IonApp,
    IonSplitPane,
    IonMenu,
    RouterLink,
    RouterLinkActive,
    IonContent,
    IonList,
    IonListHeader,
    IonMenuToggle,
    IonItem,
    IonIcon,
    IonLabel,
    IonRouterLink,
    IonRouterOutlet
  ],
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss',
})
export class AppComponent implements OnInit {
  /**
   * @description The title of the application
   */
  title = 'Decaf-ts for-angular demo';

  /**
   * @description The menu items for the application's navigation
   */
  menu: MenuItem[] = Menu;

  /**
   * @description Ionic Platform service
   */
  platform: Platform = inject(Platform);

  /**
   * @description Angular Router service
   */
  router: Router = inject(Router);

  /**
   * @description The currently active menu item
   */
  activeItem = '';

  /**
   * @description The database adapter provider
   */
  adapter = inject(DbAdapterProvider);

  /**
   * @description Flag indicating if the application has been initialized
   */
  initialized = false;

  /**
   * @description Angular Title service
   */
  private titleService: Title = inject(Title);

  /**
   * @description disable or enable menu on page
   */
  disableMenu = true;

  /**
   * @description Initializes the component
   * @summary Sets up Ionic icons and disables the menu controller
   */
  constructor() {
    addIcons(IonicIcons);
  }

  /**
   * @description Lifecycle hook that is called after data-bound properties of a directive are initialized
   * @summary Sets up router event subscriptions and initializes the application
   * @return {Promise<void>}
   */
  async ngOnInit(): Promise<void> {
    this.router.events.subscribe(async event => {
      if(event instanceof NavigationEnd) {
        const {url} = event;
        this.disableMenu = url.includes('login');
        this.setTitle(url.replace('/', '') || "login");
      }
      if (event instanceof NavigationStart)
        removeFocusTrap();
    });
    await this.initializeApp();
  }

  /**
   * @description Initializes the application
   * @summary Sets the initialized flag and sets up repositories if in development mode
   * @return {Promise<void>}
   */
  async initializeApp(): Promise<void> {
    this.initialized = true;
    const isDevelopment = isDevelopmentMode();
    if(isDevelopment) {
      for(const model of [new CategoryModel(), new EmployeeModel()] ) {
        const repository = new ForAngularRepository<typeof model>(this.adapter as DecafRepositoryAdapter, model);
        await repository.init();
      }
    }
  }

  /**
   * @description Sets the application title based on the current page
   * @summary Updates the document title with the application name and current page
   * @param {string} page - The current page URL
   */
  setTitle(page: string): void {
    const activeMenu = this.menu.find(item => item?.url?.includes(page));
    if(activeMenu)
      this.titleService.setTitle(`${title} - ${activeMenu?.title || activeMenu?.label}`);
  }
}