Angular UI: Theming

Introduction

ABP Framework provides a complete UI Theming system with the following goals:

  • Reusable application modules are developed theme-independent, so they can work with any UI theme.
  • UI theme is decided by the final application.
  • The theme is distributed via an NPM package, so it is easily upgradable.
  • The final application can customize the selected theme.

In order to accomplish these goals, ABP Framework;

  • Determines a set of base libraries used and adapted by all the themes. So, module and application developers can depend on and use these libraries without depending on a particular theme.
  • Provides a system that consists of layout parts (like navigation menus and toolbars) that is implemented by all the themes. So, the modules and the application contribute to the layout to compose a consistent application UI.

Current Themes

Currently, two themes are officially provided:

  • The Basic Theme is the minimalist theme with the plain Bootstrap style. It is open source and free.
  • The Lepton Theme is a commercial theme developed by the core ABP team and is a part of the ABP Commercial license.

Overall

The Base Libraries

All the themes must depend on the @abp/ng.theme.shared NuGet package, so they are indirectly depending on the following libraries:

These libraries are selected as the base libraries and available to the applications and modules.

Bootstrap's JavaScript part is not used since the NG Bootstrap library already provides the necessary functionalities to the Bootstrap components in a native way.

The Layout

All themes must define a layout for the application. The following image shows the user management page in the Basic Theme application layout:

basic-theme-application-layout

And the same page is shown below with the Lepton Theme application layout:

lepton-theme-application-layout

As you can see, the page is the same, but the look is completely different in the themes above.

The application layout typically includes the following parts;

  • Main menu
  • Nav items area with the following components;
    • User menu
    • Language switch dropdown
  • Page alerts
  • The page content (aka <router-outlet>)

Implementing a Theme

A theme is simply an NPM package and comes with startup templates.

The Easy Way

The easiest way to create a new theme is to add Basic Theme Source Code to your project via ABP CLI command and customize it.

You can run the following command in Angular project directory to copy the source code to your solution:

abp add-package @abp/ng.theme.basic --with-source-code

Global/Component Styles

Angular can bundle global style files and component styles with components. See the component styles guide on Angular documentation for more information.

Layout Parts

A typical layout consists of several parts. The theme should include the necessary parts in each layout.

Example: The Basic Theme has the following parts for the Application Layout

basic-theme-application-layout-parts

The application code and the modules can only show contents in the Page Content part. If they need to change the other parts (to add a menu item, to add a nav item, to change the application name in the logo area...) they should use the ABP Framework APIs.

The following sections explain the fundamental parts pre-defined by the ABP Framework and can be implemented by the themes.

It is a good practice to split the layout into components/partials, so the final application can override them partially for customization purpose.

Logo

The application object of an environment file should be configured to get the name and the logo URL of the application to render in the logo part. Additionally, LogoComponent can be replaced. See Component Replacement document for more.

The Application Startup Template has an implementation of this interface to set the values by the application developer.

Main Menu / Routes

RoutesService service is used to manage the main menu items and render them on the layout.

Example: Adding a route to the main menu

import { RoutesService, eLayoutType } from '@abp/ng.core';
import { Component } from '@angular/core';

@Component(/* component metadata */)
export class AppComponent {
  constructor(routes: RoutesService) {
    routes.add([
      {
        path: '/your-path',
        name: 'Your navigation',
        order: 101,
        iconClass: 'fas fa-question-circle',
        requiredPolicy: 'permission key here',
        layout: eLayoutType.application,
      },
      {
        path: '/your-path/child',
        name: 'Your child navigation',
        parentName: 'Your navigation',
        order: 1,
        requiredPolicy: 'permission key here',
      },
    ]);
  }
}

See the Modifying the Menu document to learn more about the navigation system.

Toolbar / Nav Items

NavItemsService service is used to get the menu's right part items and render on the layout. You can add an HTML content or Angular component as an element to render.

Example: Adding an element to right part of the menu

import { NavItemsService } from '@abp/ng.theme.shared';
import { Component } from '@angular/core';

@Component({
  template: `
    <input type="search" placeholder="Search" class="bg-transparent border-0 color-white" />
  `,
})
export class MySearchInputComponent {}


@Component(/* component metadata */)
export class AppComponent {
  constructor(private navItems: NavItemsService) {
    navItems.addItems([
      {
        id: 'MySearchInput',
        order: 1,
        component: MySearchInputComponent,
      },
      {
        id: 'SignOutIcon',
        html: '<i class="fas fa-sign-out-alt fa-lg text-white m-2"><i>',
        action: () => console.log('Clicked the sign out icon'),
        order: 101, // puts as last element
      },
    ]);
  }
}

See the How to Add an Element to Right Part of the Menu document to learn more on the nav items system.

The theme has a responsibility to add two pre-defined items to the toolbar: Language Selection and User Menu.

Language Selection

Language Selection toolbar item is generally a dropdown that is used to switch between languages. ConfigStateService is used to get the list of available languages and SessionStateService is used to learn the current language.

SessionStateService is used to get and set the current language.

Example: Get the currently selected language

import {SessionStateService} from '@abp/ng.core';

//...

constructor(private sessionState: SessionStateService) {
    const lang = this.sessionState.getLanguage()
}

Example: Set the selected language

import {SessionStateService} from '@abp/ng.core';

//...

constructor(private sessionState: SessionStateService) {
    const lang = this.sessionState.setLanguage('en')
}
User Menu

User menu is a component that can be replaceable. See an example to learn how can you replace it:

import { eThemeBasicComponents } from '@abp/ng.theme.basic';
import { NavItemsService } from '@abp/ng.theme.shared';
import { Component } from '@angular/core';

@Component({/* component metadata */})
export class AppComponent {
  constructor(private navItems: NavItemsService) {
    this.navItems.patchItem(eThemeBasicComponents.CurrentUser, { component: MyUserMenuComponent });
  }
}

ConfigStateService service can be used to obtain the application-configuration API response (e.g. getting current user or tenant).

Page Alerts

PageAlertService service is used to get the current page alerts to render on the layout. See the Page Alerts document to learn more.

In this document