myowjaYOY/ngx-admin
April 19, 2026
July 2026
Scope: This Developer Onboarding Guide covers 100 screens of the ngx-admin application. The screens assessed are: Editors (/editors), Contacts (/dashboard/contacts), Electricity (/dashboard/electricity), Kitten (/dashboard/kitten), Rooms (/dashboard/rooms), Traffic (/dashboard/traffic), Country Orders (/e-commerce/country-orders), Chart (/e-commerce/country-orders/chart), Back Side (/e-commerce/traffic-reveal-card/back-side), Map (/e-commerce/country-orders/map), Progress Section (/e-commerce/progress-section), Traffic Reveal Card (/e-commerce/traffic-reveal-card), Legend Chart (/e-commerce/legend-chart), Front Side (/e-commerce/profit-card/front-side), Traffic Cards Header (/e-commerce/traffic-reveal-card/traffic-cards-header), User Activity (/e-commerce/user-activity), Calendar Kit (/extra-components/calendar-kit), Chat (/extra-components/chat), Progress Bar (/extra-components/progress-bar), Form Inputs (/extra-components/form-inputs), Day Cell (/extra-components/calendar/day-cell), Month Cell (/extra-components/calendar-kit/month-cell), Nebular Select (/extra-components/form-inputs/nebular-select), Extra Components (/extra-components), Alert (/extra-components/alert), Visitors Analytics (/e-commerce/visitors-analytics), Spinner In Buttons (/extra-components/spinner/spinner-in-buttons), Maps (/maps), Spinner Color (/extra-components/spinner/spinner-color), Leaflet (/maps/leaflet), Pages (/pages), Tables (/tables), Dashboard (/dashboard), Icons (/ui-features/icons), E Commerce (/e-commerce), Footer (/@theme/components/footer), Chart Panel Header (/e-commerce/charts-panel/chart-panel-header), Spinner (/extra-components/spinner), Chart Panel Summary (/e-commerce/charts-panel/chart-panel-summary), Player (/dashboard/rooms/player), Header (/@theme/components/header), Calendar (/extra-components/calendar), Form Layouts (/forms/form-layouts), Window Form (/modal-overlays/window/window-form), Room Selector (/dashboard/rooms/room-selector), Ui Features (/ui-features), Traffic Bar (/e-commerce/traffic-reveal-card/front-side/traffic-bar), Spinner In Tabs (/extra-components/spinner/spinner-in-tabs), Interactive Progress Bar (/extra-components/progress-bar/interactive-progress-bar), Datepicker (/forms/datepicker), Form Inputs (/forms/form-inputs), Charts (/e-commerce/charts-panel/charts), Bubble (/maps/bubble), Profit Card (/e-commerce/profit-card), Slide Out (/e-commerce/slide-out), Back Side (/e-commerce/profit-card/back-side), Smart Table (/tables/smart-table), Typography (/ui-features/typography), Modal Overlays (/modal-overlays), Visitors Statistics (/e-commerce/visitors-analytics/visitors-statistics), Ckeditor (/editors/ckeditor), Grid (/ui-features/grid), Echarts (/charts/echarts), Not Found (/miscellaneous/not-found), Visitors Analytics Chart (/e-commerce/visitors-analytics/visitors-analytics-chart), Tiny Mce (/editors/tiny-mce), Front Side (/e-commerce/earning-card/front-side), Electricity Chart (/dashboard/electricity/electricity-chart), Search Fields (/ui-features/search-fields), Dialog Name Prompt (/modal-overlays/dialog/dialog-name-prompt), Tooltip (/modal-overlays/tooltip), Search Map (/maps/search-map), Map (/maps/search-map/map), Window (/modal-overlays/window), Dialog (/modal-overlays/dialog), Miscellaneous (/miscellaneous), Gmaps (/maps/gmaps), Popovers (/modal-overlays/popovers), Search (/maps/search-map/search), Tree Grid (/tables/tree-grid), Showcase Dialog (/modal-overlays/dialog/showcase-dialog), Toastr (/modal-overlays/toastr), Search Input (/@theme/components/search-input), Tiny Mce (/@theme/components/tiny-mce), Status Card (/dashboard/status-card), Front Side (/e-commerce/traffic-reveal-card/front-side), Earning Card (/e-commerce/earning-card), Solar (/dashboard/solar), Security Cameras (/dashboard/security-cameras), Temperature Dragger (/dashboard/temperature/temperature-dragger), Charts (/charts), Chartjs (/charts/chartjs), Temperature (/dashboard/temperature), D3 (/charts/d3), Back Side (/e-commerce/earning-card/back-side), Charts Panel (/e-commerce/charts-panel), Forms (/forms), Weather (/dashboard/weather), Buttons (/forms/buttons), Spinner Sizes (/extra-components/spinner/spinner-sizes). Components, patterns, and features not included in the assessed documentation are outside the scope of this document. This document reflects a partial view of the application.
Document disclosure: Generated by DocAgent — automated codebase documentation analysis. Subject matter expert review is recommended before distribution.
What this application does: ngx-admin is an Angular-based admin dashboard application built on the Nebular UI framework. It serves as both a feature-rich dashboard template and a component showcase, targeting developers and administrators who need to monitor IoT/smart-home data, e-commerce analytics, and system metrics. The application demonstrates a wide range of UI patterns including interactive charts (ECharts, Chart.js, D3), data tables, map visualizations, form components, modal overlays, and real-time data widgets.
Tech stack at a glance:
@nebular/theme) — UI component library providing cards, menus, dialogs, theming, and breakpoint servicesngx-echarts) — Primary charting library for interactive data visualizationsng2-charts) — Secondary charting library@asymmetrik/ngx-leaflet) — Interactive map renderingng2-smart-table — Feature-rich data grid with inline CRUDHow this guide is organized: Sections marked (Tutorial) walk you through an experience step by step. Sections marked (How-to) give you procedures for specific tasks. Sections marked (Reference) are lookup tables and factual descriptions. Sections marked (Explanation) provide context and rationale for architectural decisions.
Who to ask:
[Team Lead: name][Code Owner for dashboard/e-commerce features: name][Code Owner for @core data services: name][Code Owner for @theme layout components: name][Not documented — WHO: Team Lead; WHAT: Names and contact details for each code owner area listed above; WHERE: Replace the placeholder entries in the "Who to ask" list above.]
(Tutorial)
This section walks you from a fresh machine to a running local development environment. Follow every step in order.
Before you start, install the following tools. Verify each version after installation.
| Tool | Required Version | Install Command |
|---|---|---|
| Node.js | 18.x or 20.x LTS | nvm install 20 (using nvm) |
| npm | 9.x or 10.x (bundled with Node) | Included with Node.js |
| Angular CLI | 16.x or 17.x | npm install -g @angular/cli |
| Git | Any recent version | git-scm.com |
Verify your setup:
node --version # Should print v18.x.x or v20.x.x
npm --version # Should print 9.x.x or 10.x.x
ng version # Should print Angular CLI version[Not documented — WHO: Team Lead; WHAT: The exact Node.js and Angular CLI versions pinned for this project (check .nvmrc, package.json engines field, or CI configuration); WHERE: Replace the version ranges in the Prerequisites table above with exact pinned versions.]
git clone [repository-url]
cd ngx-admin
npm installExpected output: npm prints a dependency tree and completes without errors. You may see deprecation warnings — these are expected and non-blocking.
[Not documented — WHO: Team Lead; WHAT: The repository URL; WHERE: Replace [repository-url] in the clone command above.]
This application uses Angular's environment.ts pattern for environment-specific configuration. (inferred from Angular CLI conventions — verify against src/environments/ directory)
# Check what environment files exist
ls src/environments/You should see at minimum environment.ts (development) and environment.prod.ts (production).
Variables you may need to configure:
| Variable | Purpose | Where to Get It |
|---|---|---|
| Google Maps API key | Required for /maps/gmaps and /maps/search-map screens |
Ask your team lead |
| TinyMCE CDN base path | Defaults to //cdn.ckeditor.com/4.6.2/full-all/ — may need updating |
Check src/app/@theme/components/tiny-mce/ |
[Not documented — WHO: Team Lead; WHAT: The complete list of environment variables required for local development, including any API keys, feature flags, or backend URLs; WHERE: Insert a complete environment variable table in this section.]
For most screens in this application, no environment configuration is required — the majority of data is served from in-memory mock services.
ng serveExpected output:
** Angular Live Development Server is listening on localhost:4200 **
✔ Compiled successfully.
The first build may take 30–60 seconds. Subsequent builds are faster due to incremental compilation.
http://localhost:4200.[Screenshot: The ngx-admin dashboard home page with the Nebular sidebar visible on the left and e-commerce KPI cards in the main content area.]
If the page loads but charts appear blank, wait 2–3 seconds — ECharts initializes asynchronously after the Nebular theme service emits. This is expected behavior.
(Reference)
src/
├── app/
│ ├── @core/ — Core module: data services, utilities, mock providers
│ │ ├── data/ — Abstract data service classes (injection tokens)
│ │ ├── mock/ — Concrete mock implementations of data services
│ │ └── utils/ — Shared utilities (LayoutService, AnalyticsService)
│ ├── @theme/ — Theme module: shared layout components
│ │ └── components/ — Header, Footer, SearchInput, TinyMCE wrapper
│ └── pages/ — Feature pages organized by route segment
│ ├── dashboard/ — IoT/smart-home dashboard screens
│ ├── e-commerce/ — E-commerce analytics screens
│ ├── extra-components/ — Component showcase screens
│ ├── forms/ — Form demonstration screens
│ ├── maps/ — Map integration screens
│ ├── charts/ — Chart library showcase screens
│ ├── tables/ — Data table screens
│ ├── editors/ — Rich text editor screens
│ ├── modal-overlays/ — Dialog, window, popover, toastr screens
│ ├── ui-features/ — Typography, icons, grid screens
│ └── miscellaneous/ — 404 and other utility screens
├── assets/
│ ├── map/ — GeoJSON world map data (world.json)
│ ├── leaflet-countries/ — GeoJSON country boundaries for Leaflet
│ └── skins/ — TinyMCE editor skin assets
└── environments/
├── environment.ts — Development environment config
└── environment.prod.ts — Production environment config
| File | Purpose | When You'll Touch It |
|---|---|---|
src/app/pages/pages.component.ts |
Root shell for all authenticated pages; owns the sidebar MENU_ITEMS |
When adding a new top-level navigation section |
src/app/pages/pages-menu.ts |
Static MENU_ITEMS array defining the sidebar navigation tree |
Every time you add a new page that needs a sidebar link |
src/app/@core/data/ |
Abstract data service classes (e.g., users.ts, electricity.ts) |
When adding a new data domain or extending an existing one |
src/app/@core/mock/ |
Concrete mock implementations of all data services | When you need to change what data the app displays in development |
src/app/@core/utils/layout.service.ts |
Broadcasts layout size change events; consumed by all chart components for resize | When debugging chart resize issues or adding a new chart |
src/app/@theme/components/header/header.component.ts |
Global header with theme switcher, user menu, and sidebar toggle | When modifying global navigation or theme switching behavior |
src/app/pages/e-commerce/charts-panel/charts-panel.component.ts |
Reference implementation of the full data-loading + chart pattern | When building a new data-driven chart page |
src/app/pages/dashboard/temperature/temperature-dragger/temperature-dragger.component.ts |
Complex custom SVG interactive widget | When building custom SVG-based interactive components |
src/app/pages/tables/smart-table/smart-table.component.ts |
Reference implementation of ng2-smart-table with inline CRUD |
When building a new data table screen |
src/app/pages/modal-overlays/dialog/dialog.component.ts |
Reference implementation of NbDialogService patterns |
When adding dialogs, windows, or overlays |
src/app/pages/maps/search-map/search/search.component.ts |
Google Places Autocomplete integration with NgZone |
When integrating third-party DOM-manipulating libraries |
src/app/pages/e-commerce/country-orders/map/map.component.ts |
Leaflet map with GeoJSON overlay and theme integration | When building Leaflet-based map screens |
src/assets/map/world.json |
GeoJSON world map data used by the Bubble map screen | When updating map data |
src/environments/environment.ts |
Development environment variables | When adding new API keys or feature flags |
| What | Convention | Example |
|---|---|---|
| Component files | kebab-case.component.ts |
electricity-chart.component.ts |
| Component selectors | ngx- prefix + kebab-case |
ngx-electricity-chart |
| Nebular component selectors | nb- prefix |
nb-card, nb-icon |
| Abstract data service classes | PascalCase + Data suffix |
ElectricityData, UserData |
| Mock service classes | PascalCase + MockService suffix |
ElectricityMockService |
| Feature directories | kebab-case matching route segment | e-commerce/, extra-components/ |
| Interface names | PascalCase, no prefix | Electricity, TrafficList, Camera |
| Enum names | PascalCase | NgxLegendItemColor |
| Constants | SCREAMING_SNAKE_CASE |
MENU_ITEMS, VIEW_BOX_SIZE |
| Private component properties | camelCase | alive, echartsIntance |
@Input() properties |
camelCase | chartValue, frontCardData |
@Output() emitters |
camelCase, event-noun pattern | selectPie, positionChanged |
| SCSS files | Same name as component | electricity-chart.component.scss |
(Explanation)
What it is: Angular with a hierarchical router using nested <router-outlet> elements.
Why it was chosen: ngx-admin is built on the Angular framework. The nested routing approach allows feature sections (e.g., /e-commerce, /dashboard) to share a common layout shell while independently loading their child screens.
How it affects your work: Every feature section has a "shell component" — a component whose only job is to render <router-outlet>. When you add a new screen, you add it as a child route under the appropriate shell. You never add UI to a shell component itself.
Where to see it: PagesComponent (/pages) is the top-level shell. ECommerceComponent (/e-commerce), DashboardComponent (/dashboard), MapsComponent (/maps), TablesComponent (/tables), ChartsComponent (/charts), FormsComponent (/forms), EditorsComponent (/editors), ExtraComponentsComponent (/extra-components), ModalOverlaysComponent (/modal-overlays), UiFeaturesComponent (/ui-features), and MiscellaneousComponent (/miscellaneous) are all section-level shells.
What it is: All UI chrome (cards, buttons, menus, dialogs, icons, tabs, inputs) comes from the Nebular component library (@nebular/theme), an open-source Angular UI kit built on the Eva Design System.
Why it was chosen: Nebular provides a cohesive, themeable component set that integrates tightly with Angular's DI system. It ships with four built-in themes (default, dark, cosmic, corporate) and a NbThemeService that allows components to react to theme changes at runtime.
How it affects your work: When you need a card, use <nb-card>. When you need a button, use <button nbButton>. When you need a dialog, use NbDialogService. Do not introduce a second UI library. All Nebular components use the nb- selector prefix.
Where to see it: Every screen in the application uses Nebular components. The PagesComponent template uses <nb-menu> for the sidebar. The HeaderComponent uses NbThemeService and NbSidebarService.
What it is: Every data domain (electricity, users, traffic, cameras, etc.) is defined as an Angular abstract class in src/app/@core/data/. The abstract class acts as an Angular injection token. A concrete mock implementation is registered in the module and injected at runtime.
Why it was chosen: This pattern decouples components from their data sources. Swapping from mock data to a real HTTP API requires only changing the provider registration in the module — no component code changes. It also makes unit testing straightforward: inject a mock that returns controlled Observable values.
How it affects your work: When you need data in a component, inject the abstract class (e.g., ElectricityData), not the concrete implementation. When you need to change what data is returned, edit the mock in src/app/@core/mock/. When you need a new data domain, create a new abstract class in src/app/@core/data/ and a corresponding mock in src/app/@core/mock/.
Where to see it: src/app/@core/data/electricity.ts (abstract), src/app/@core/mock/electricity.service.ts (concrete mock). The pattern is used for every data domain: users.ts, traffic-chart.ts, security-cameras.ts, temperature-humidity.ts, etc.
What it is: Component-local state held as class properties. No global state management library (NgRx, Akita, NGXS) is used.
Why it was chosen: The application is a dashboard template with largely independent screens. Each screen manages its own data lifecycle. The complexity of a global store is not warranted for this use case.
How it affects your work: State lives on the component class as plain TypeScript properties. When a component is destroyed, its state is lost. If you need to share state between sibling components, use a shared service or pass data through a common parent via @Input()/@Output() bindings.
Where to see it: ElectricityComponent holds listData, chartData, type, and currentTheme as class properties. RoomsComponent holds expanded, selected, isDarkTheme, and breakpoint as class properties.
What it is: RxJS Observable subscriptions, often using forkJoin for parallel fetches. Subscriptions are managed with the takeWhile(() => this.alive) operator and ngOnDestroy.
Why it was chosen: Angular's ecosystem is built around RxJS. The abstract data services return Observable<T>, making RxJS the natural choice. forkJoin is used when a screen needs multiple datasets before it can render (e.g., the Electricity screen needs both list data and chart data simultaneously).
How it affects your work: You must always clean up subscriptions. The codebase uses the alive boolean flag pattern with takeWhile. Modern Angular (16+) prefers takeUntilDestroyed() — either pattern is acceptable, but be consistent within a file. Never subscribe without a cleanup mechanism.
Where to see it: ContactsComponent uses forkJoin + takeWhile. ElectricityComponent uses forkJoin + takeWhile. SecurityCamerasComponent uses takeUntil(this.destroy$) — a slightly more modern variant.
What it is: Nebular's NbThemeService provides two reactive streams: getJsTheme() (emits the current theme's JavaScript variable map) and onMediaQueryChange() (emits when the viewport crosses a breakpoint). Components subscribe to these to adapt their appearance and layout.
Why it was chosen: The application supports four themes (default, dark, cosmic, corporate). Charts and custom SVG components cannot be styled with CSS alone — they need JavaScript-accessible color values. NbThemeService.getJsTheme() provides those values reactively.
How it affects your work: Any component that renders a chart or custom SVG must subscribe to getJsTheme() to get theme-appropriate colors. Any component that needs to respond to viewport changes (e.g., changing button sizes, adjusting card sizes) subscribes to onMediaQueryChange(). Both subscriptions must be cleaned up in ngOnDestroy.
Where to see it: ElectricityChartComponent, OrdersChartComponent, ProfitChartComponent, VisitorsAnalyticsChartComponent, and SolarComponent all subscribe to getJsTheme(). RoomsComponent, CountryOrdersComponent, and TypographyComponent subscribe to onMediaQueryChange().
What it is: Apache ECharts is the primary charting library, integrated via the ngx-echarts Angular directive. Charts are configured by building an options object from theme variables and input data, then binding it to [options] on a <div echarts> element.
Why it was chosen: ECharts provides a rich set of chart types with good performance. The ngx-echarts wrapper provides Angular-idiomatic bindings. The LayoutService provides a debounced resize event that chart components subscribe to, ensuring charts reflow correctly when the sidebar is toggled.
How it affects your work: Every chart component follows the same lifecycle: (1) subscribe to getJsTheme() in ngAfterViewInit with a delay(1), (2) build the options object using theme variables and @Input() data, (3) store the ECharts instance via (chartInit), (4) subscribe to LayoutService.onSafeChangeLayoutSize() to call echartsInstance.resize(). See Section 5.6 for the full pattern.
Where to see it: OrdersChartComponent, ProfitChartComponent, ElectricityChartComponent, EarningLiveUpdateChartComponent, VisitorsAnalyticsChartComponent, TrafficBarChartComponent.
What it is: A component whose template contains only <router-outlet> and whose class body is empty. It exists solely to provide a named URL segment and a rendering target for child routes.
Why it was chosen: Angular's router requires a component at every route level. Shell components satisfy this requirement without adding unnecessary logic or UI to route segments that are purely structural.
How it affects your work: When you see a component with an empty class body and a <router-outlet> template, do not add logic to it. If you need shared UI across all screens in a section (e.g., a section header), add it to the shell component's template. If you need shared data, use a service.
Where to see it: EditorsComponent, ExtraComponentsComponent, MapsComponent, TablesComponent, ChartsComponent, FormsComponent, ModalOverlaysComponent, UiFeaturesComponent, MiscellaneousComponent.
What it is: Two patterns are used to prevent memory leaks from RxJS subscriptions: the alive flag pattern (takeWhile(() => this.alive) + ngOnDestroy sets alive = false) and the destroy$ subject pattern (takeUntil(this.destroy$) + ngOnDestroy calls destroy$.next()).
Why it was chosen: Angular components are destroyed when the user navigates away. Any active RxJS subscription that is not cleaned up will continue to run and attempt to update the destroyed component's state, causing memory leaks and potential errors.
How it affects your work: Every subscription you create must be cleaned up. Prefer the destroy$ subject pattern for new code — it is more reliable than takeWhile because it completes the observable immediately rather than waiting for the next emission. See Section 5.2 for the pattern.
Where to see it: SecurityCamerasComponent uses destroy$. Most other components use alive + takeWhile.
(Reference + Explanation)
Type: Routing Pattern
When to use: When you need a URL segment that groups child routes but has no UI of its own.
The pattern:
import { Component } from '@angular/core';
@Component({
selector: 'ngx-my-section',
template: '<router-outlet></router-outlet>',
})
export class MySectionComponent {}Key rules:
<router-outlet>.@Input(), @Output(), services, or lifecycle hooks.Example in the codebase: EditorsComponent, MapsComponent, ChartsComponent.
Type: Component Pattern
When to use: When a page needs to fetch data from a service and display it.
The pattern:
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { MyData } from '../../@core/data/my-data';
@Component({
selector: 'ngx-my-page',
templateUrl: './my-page.component.html',
})
export class MyPageComponent implements OnInit, OnDestroy {
// Declare state properties
items: MyItem[];
// Subject used to signal subscription cleanup
private destroy$ = new Subject<void>();
constructor(private myService: MyData) {}
ngOnInit() {
// Fetch data here, not in the constructor
this.myService.getItems()
.pipe(takeUntil(this.destroy$))
.subscribe(items => {
this.items = items;
});
}
ngOnDestroy() {
// Signal all takeUntil operators to complete
this.destroy$.next();
this.destroy$.complete();
}
}Key rules:
ngOnInit, not in the constructor.takeUntil(this.destroy$) on every subscription.ngOnDestroy and call destroy$.next()..subscribe() call: .subscribe({ next: ..., error: err => console.error(err) }).Example in the codebase: ContactsComponent, ElectricityComponent, SecurityCamerasComponent.
Type: Component Pattern
When to use: When a component only renders data passed to it via @Input() and emits events via @Output().
The pattern:
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'ngx-my-card',
templateUrl: './my-card.component.html',
})
export class MyCardComponent {
// All data comes in via @Input()
@Input() title: string;
@Input() value: number;
// User actions go out via @Output()
@Output() selected = new EventEmitter<string>();
onSelect(id: string) {
this.selected.emit(id);
}
}Key rules:
ngOnInit data fetching.@Input().@Output().ChangeDetectionStrategy.OnPush for performance.Example in the codebase: ChartPanelSummaryComponent, TrafficBarComponent, LegendChartComponent, StatusCardComponent.
Type: Data Fetching Pattern
When to use: When you need to add a new data domain to the application.
The pattern:
// src/app/@core/data/my-domain.ts
import { Observable } from 'rxjs';
export interface MyItem {
id: number;
name: string;
value: number;
}
export abstract class MyDomainData {
abstract getItems(): Observable<MyItem[]>;
abstract getItemById(id: number): Observable<MyItem>;
}// src/app/@core/mock/my-domain.service.ts
import { Injectable } from '@angular/core';
import { of } from 'rxjs';
import { MyDomainData, MyItem } from '../data/my-domain';
@Injectable()
export class MyDomainMockService extends MyDomainData {
private items: MyItem[] = [
{ id: 1, name: 'Item A', value: 42 },
];
getItems() {
return of(this.items);
}
getItemById(id: number) {
return of(this.items.find(i => i.id === id));
}
}Key rules:
src/app/@core/data/.src/app/@core/mock/.{ provide: MyDomainData, useClass: MyDomainMockService }.MyDomainData, never MyDomainMockService.Example in the codebase: ElectricityData / ElectricityMockService, UserData / UsersMockService.
Type: Component Pattern
When to use: When a component needs to adapt its colors or styles based on the active Nebular theme.
The pattern:
import { Component, OnDestroy } from '@angular/core';
import { NbThemeService } from '@nebular/theme';
import { takeWhile } from 'rxjs/operators';
@Component({ selector: 'ngx-my-widget', templateUrl: './my-widget.component.html' })
export class MyWidgetComponent implements OnDestroy {
private alive = true;
currentTheme: string;
themeColors: any; // typed as any; use specific interface if available
constructor(private themeService: NbThemeService) {
this.themeService.getJsTheme()
.pipe(takeWhile(() => this.alive))
.subscribe(config => {
this.currentTheme = config.name;
// Access theme-specific variables, e.g. config.variables.myWidget
this.themeColors = config.variables.myWidget;
});
}
ngOnDestroy() {
this.alive = false;
}
}Key rules:
getJsTheme() to get theme variables, not just the theme name.config.variables.[componentName] — check the Nebular theme configuration for available namespaces.ngOnDestroy.getJsTheme() in ngAfterViewInit with delay(1), not in the constructor.Example in the codebase: KittenComponent, TrafficComponent, DashboardComponent, HeaderComponent.
Type: Data Fetching Pattern + Component Pattern
When to use: When you need to render an interactive chart using Apache ECharts.
The pattern:
import { Component, Input, OnChanges, AfterViewInit, OnDestroy } from '@angular/core';
import { NbThemeService } from '@nebular/theme';
import { LayoutService } from '../../../@core/utils/layout.service';
import { takeWhile, delay } from 'rxjs/operators';
@Component({
selector: 'ngx-my-chart',
template: `<div echarts [options]="option" class="echart"
(chartInit)="onChartInit($event)"></div>`,
})
export class MyChartComponent implements AfterViewInit, OnChanges, OnDestroy {
@Input() chartData: number[];
option: any = {};
private echartsInstance: any;
private alive = true;
constructor(
private theme: NbThemeService,
private layoutService: LayoutService,
) {
// Subscribe to layout resize events
this.layoutService.onSafeChangeLayoutSize()
.pipe(takeWhile(() => this.alive))
.subscribe(() => this.resizeChart());
}
ngAfterViewInit() {
// Delay(1) avoids ExpressionChangedAfterItHasBeenCheckedError
this.theme.getJsTheme()
.pipe(takeWhile(() => this.alive), delay(1))
.subscribe(config => {
const eTheme: any = config.variables.myChart;
this.setOptions(eTheme);
});
}
ngOnChanges() {
// Only update after initial options are set
if (this.option.series) {
this.updateChartOptions();
}
}
onChartInit(ec: any) {
this.echartsInstance = ec;
}
private setOptions(eTheme: any) {
this.option = {
// Full ECharts configuration using eTheme colors and this.chartData
};
}
private updateChartOptions() {
// Partial update — only change what changed
if (this.echartsInstance) {
this.echartsInstance.setOption({ series: [{ data: this.chartData }] });
}
}
private resizeChart() {
if (this.echartsInstance) {
setTimeout(() => this.echartsInstance.resize(), 0);
}
}
ngOnDestroy() {
this.alive = false;
}
}Key rules:
getJsTheme() in ngAfterViewInit, not the constructor, and always pipe through delay(1).(chartInit) — you need it for resize and partial updates.echartsInstance.setOption() for data updates after initialization (partial update, not full re-render).ngOnChanges with if (this.option.series) to prevent premature updates.resizeChart() with if (this.echartsInstance).echartsInstance.resize() in setTimeout(..., 0) to allow DOM reflow first.LayoutService.onSafeChangeLayoutSize() for resize events.Example in the codebase: OrdersChartComponent, ProfitChartComponent, ElectricityChartComponent, TrafficBarChartComponent.
Type: Component Pattern
When to use: When you need a data table with built-in sorting, filtering, and inline CRUD.
The pattern:
import { Component } from '@angular/core';
import { LocalDataSource } from 'ng2-smart-table';
import { MyData } from '../../@core/data/my-data';
@Component({
selector: 'ngx-my-table',
templateUrl: './my-table.component.html',
})
export class MyTableComponent {
settings = {
actions: {
add: true,
edit: true,
delete: true,
confirmDelete: true, // Enables (deleteConfirm) output event
},
columns: {
id: { title: 'ID', type: 'number' },
name: { title: 'Name', type: 'string' },
},
};
source: LocalDataSource;
constructor(private service: MyData) {
this.source = new LocalDataSource(this.service.getData());
}
onDeleteConfirm(event): void {
if (window.confirm('Are you sure?')) {
event.confirm.resolve();
} else {
event.confirm.reject();
}
}
}Key rules:
LocalDataSource for in-memory data; use ServerDataSource for paginated server-side data.confirmDelete: true in settings.actions and handle the (deleteConfirm) event to prevent accidental deletions.settings object is static — define it at class initialization time.'<i class="nb-plus"></i>') — use Nebular icon classes.Example in the codebase: SmartTableComponent.
Type: Component Pattern
When to use: When you need to open a modal dialog that can return a value to the caller.
The pattern:
// The dialog content component
import { Component, Input } from '@angular/core';
import { NbDialogRef } from '@nebular/theme';
@Component({
selector: 'ngx-my-dialog',
template: `
<nb-card>
<nb-card-header>{{ title }}</nb-card-header>
<nb-card-body><!-- content --></nb-card-body>
<nb-card-footer>
<button nbButton (click)="cancel()">Cancel</button>
<button nbButton status="primary" (click)="submit(result)">OK</button>
</nb-card-footer>
</nb-card>
`,
})
export class MyDialogComponent {
@Input() title: string;
result: string;
constructor(protected ref: NbDialogRef<MyDialogComponent>) {}
cancel() { this.ref.close(); }
submit(value: string) { this.ref.close(value); }
}
// The caller component
import { NbDialogService } from '@nebular/theme';
this.dialogService.open(MyDialogComponent, {
context: { title: 'My Dialog' },
}).onClose.subscribe(result => {
if (result) { /* handle result */ }
});Key rules:
NbDialogRef<T> into the dialog component to control its lifecycle.context object in open() — these map to @Input() properties.onClose observable on the returned NbDialogRef.close() call with no argument signals cancellation; close(value) signals success.Example in the codebase: ShowcaseDialogComponent, DialogNamePromptComponent, DialogComponent.
(How-to)
When: You need to add a new screen to an existing section (e.g., a new dashboard widget page).
Steps:
Create a new directory under the appropriate section in src/app/pages/:
mkdir src/app/pages/dashboard/my-new-screenCreate the component files:
ng generate component pages/dashboard/my-new-screen --module pages/dashboard/dashboard.moduleExpected result: Angular CLI creates my-new-screen.component.ts, .html, .scss, and registers it in DashboardModule.
Add the route to the section's routing module (e.g., dashboard-routing.module.ts):
{ path: 'my-new-screen', component: MyNewScreenComponent }Add a sidebar navigation entry in src/app/pages/pages-menu.ts:
{
title: 'My New Screen',
icon: 'some-icon-outline',
link: '/pages/dashboard/my-new-screen',
}Navigate to http://localhost:4200/pages/dashboard/my-new-screen and verify the page loads.
Watch out for: The route path in pages-menu.ts must include the full path from the root (e.g., /pages/dashboard/my-new-screen), not just the relative segment.
When: You need to expose a new type of data to components.
Steps:
Create the abstract class in src/app/@core/data/:
// src/app/@core/data/my-domain.ts
export interface MyItem { id: number; name: string; }
export abstract class MyDomainData {
abstract getItems(): Observable<MyItem[]>;
}Create the mock implementation in src/app/@core/mock/:
// src/app/@core/mock/my-domain.service.ts
@Injectable()
export class MyDomainMockService extends MyDomainData {
getItems() { return of([{ id: 1, name: 'Test' }]); }
}Register the provider in the core module (find the providers array in src/app/@core/core.module.ts or equivalent):
{ provide: MyDomainData, useClass: MyDomainMockService }Inject MyDomainData in your component and call getItems().
Watch out for: Always inject the abstract class, not the mock. If you inject the mock directly, swapping to a real HTTP service later will require changing every component that uses it.
When: You need to change what data a screen displays.
Steps:
src/app/@core/mock/.src/app/@core/data/ first, then implement it in the mock.Watch out for: Some mock services return static arrays defined at the top of the file. Others compute data dynamically. Check whether the method uses of(staticArray) or generates data on each call.
When: You need a form that collects user input and validates it before submission.
Steps:
Import ReactiveFormsModule in your feature module.
Build the form in the component class:
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
name: ['', [Validators.required, Validators.minLength(3)]],
email: ['', [Validators.required, Validators.email]],
});
}
onSubmit() {
if (this.form.valid) {
// handle submission
}
}Bind the form in the template using Nebular inputs:
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<input nbInput formControlName="name" placeholder="Name">
<ng-container *ngIf="form.get('name').invalid && form.get('name').touched">
<p class="caption status-danger">Name is required (min 3 chars)</p>
</ng-container>
<button nbButton type="submit" [disabled]="form.invalid">Submit</button>
</form>Watch out for: The WindowFormComponent in the codebase is an example of what not to do — it renders form fields with no Angular form binding and no validation. Always use Reactive Forms for any form that submits data.
When: You need to protect a new route so only authenticated users can access it.
Steps:
Locate the existing AuthGuard (or equivalent) in the application.
[Not documented — WHO: Team Lead; WHAT: The name and file path of the authentication route guard used in this project; WHERE: Replace step 1 with the actual guard name and import path.]
Add the guard to your route definition:
{
path: 'my-new-screen',
component: MyNewScreenComponent,
canActivate: [AuthGuard],
}Verify that navigating to the route while unauthenticated redirects to the login page.
Watch out for: Do not add authentication logic inside the component itself. Route guards are the correct place for access control in Angular.
When: You want to verify your changes haven't broken existing functionality.
Steps:
Run the unit test suite:
ng testExpected result: Karma opens a browser window and runs all .spec.ts files. Results appear in the terminal.
Run tests for a specific file:
ng test --include='**/electricity.component.spec.ts'Run end-to-end tests (if configured):
ng e2eWatch out for: The assessed documentation found no co-located .spec.ts test files for any of the 100 screens. This means the test suite may be empty or tests may live in a separate directory. Ask your team lead for the testing setup before assuming ng test will produce meaningful results.
[Not documented — WHO: Team Lead; WHAT: Whether a test suite exists, where test files are located, and which test runner (Karma, Jest, Cypress, Playwright) is configured; WHERE: Update the steps in this section with the actual test commands.]
When: A screen is not displaying data and you suspect the data service is failing silently.
Steps:
Open the component that fetches the data and locate the .subscribe() call.
Add a temporary error handler:
this.myService.getData()
.pipe(takeUntil(this.destroy$))
.subscribe({
next: data => { this.data = data; },
error: err => { console.error('Data service failed:', err); },
});Open the browser DevTools console and reload the page. Look for the error message.
If no error appears, check whether the mock service is returning an empty array. Open src/app/@core/mock/ and inspect the relevant service.
If the service returns data but the template shows nothing, check whether the template has a null guard (e.g., *ngIf="data" or data?.property).
Watch out for: The majority of data service subscriptions in this codebase have no error handler. Silent failures are the most common cause of blank screens. Always add an error handler when debugging.
When: You've added a new page and need it to appear in the sidebar.
Steps:
Open src/app/pages/pages-menu.ts.
Find the appropriate section in the MENU_ITEMS array (e.g., under "Dashboard" or "E-commerce").
Add a new NbMenuItem entry:
{
title: 'My New Screen',
icon: 'activity-outline', // Eva icon name
link: '/pages/dashboard/my-new-screen',
}Save the file. The dev server will hot-reload and the new item will appear in the sidebar.
Watch out for: The link property must be the full absolute path starting with /pages/. Using a relative path will cause the menu item to not highlight correctly when active.
(How-to)
Symptoms: A screen with an ECharts chart loads but the chart area is empty or shows a blank canvas. The chart may appear after switching themes or resizing the window.
Cause: The getJsTheme() subscription in ngAfterViewInit fires before the ECharts instance is ready, or the options object is set before the theme resolves. The delay(1) operator is missing or the ngOnChanges guard (if (this.option.series)) is absent.
Fix:
getJsTheme() is called in ngAfterViewInit, not the constructor.delay(1) is piped before the subscribe:
this.theme.getJsTheme()
.pipe(takeWhile(() => this.alive), delay(1))
.subscribe(config => { this.setOptions(config.variables); });ngOnChanges guards against premature updates:
ngOnChanges() {
if (this.option.series) { this.updateChartOptions(); }
}url() References Break on Non-Root DeploymentsSymptoms: Custom SVG components (like TemperatureDraggerComponent) render incorrectly — clip paths or gradients are missing. The issue appears only when the app is deployed at a sub-path (e.g., /app/ instead of /).
Cause: SVG url(#id) references require absolute URLs in some browsers. When the app is deployed at a non-root base href, the relative url(#id) syntax breaks.
Fix: Use the getUrlPath(id) pattern from TemperatureDraggerComponent:
constructor(
private location: Location,
private locationStrategy: LocationStrategy,
) {}
getUrlPath(id: string): string {
const baseHref = this.locationStrategy.getBaseHref().replace(/\/$/, '');
const path = this.location.path().replace(/\/$/, '');
return `url(${baseHref}${path}#${id})`;
}Use getUrlPath('my-clip-path') in the template instead of url(#my-clip-path).
TypeError: Cannot read properties of undefined on Data BindingSymptoms: The browser console shows TypeError: Cannot read properties of undefined (reading 'someProperty'). The screen renders blank or partially.
Cause: A component's @Input() data is undefined when the template first renders. This happens because data is loaded asynchronously and the template tries to access a property before the data arrives.
Fix:
Add null guards in the template using the safe navigation operator:
<span>{{ item?.name }}</span>Or use *ngIf to defer rendering until data is available:
<div *ngIf="items">
<div *ngFor="let item of items">{{ item.name }}</div>
</div>Or provide a default value in the component:
items: MyItem[] = [];Symptoms: The browser DevTools memory profiler shows growing heap usage. Console warnings about "setState on unmounted component" equivalents. Stale data appearing after navigation.
Cause: An RxJS subscription was created but never unsubscribed. This is the most common bug in this codebase — many components use takeWhile(() => this.alive) but forget to set this.alive = false in ngOnDestroy, or set it to true instead of false (a known bug in ECommerceProgressSectionComponent).
Fix:
Verify ngOnDestroy is implemented and sets alive = false:
ngOnDestroy() {
this.alive = false; // NOT true
}For new code, prefer the destroy$ pattern which is harder to get wrong:
private destroy$ = new Subject<void>();
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}ExpressionChangedAfterItHasBeenCheckedError in Chart ComponentsSymptoms: Angular throws ExpressionChangedAfterItHasBeenCheckedError in the browser console when a chart component initializes. The chart may still render, but the error indicates a change detection timing problem.
Cause: The getJsTheme() subscription fires synchronously during ngAfterViewInit and updates a binding that Angular has already checked in the current change detection cycle.
Fix: Add delay(1) to the theme subscription in ngAfterViewInit:
ngAfterViewInit() {
this.theme.getJsTheme()
.pipe(takeWhile(() => this.alive), delay(1)) // delay(1) is the fix
.subscribe(config => {
this.setOptions(config.variables);
});
}Symptoms: After collapsing or expanding the sidebar, an ECharts chart remains at its old size — it either overflows its container or leaves empty space.
Cause: ECharts calculates its canvas dimensions at render time. When the sidebar toggles, the container width changes, but ECharts is not notified.
Fix:
Verify the component subscribes to LayoutService.onSafeChangeLayoutSize():
this.layoutService.onSafeChangeLayoutSize()
.pipe(takeWhile(() => this.alive))
.subscribe(() => this.resizeChart());Verify resizeChart() wraps the resize call in setTimeout:
private resizeChart() {
if (this.echartsInstance) {
setTimeout(() => this.echartsInstance.resize(), 0);
}
}Verify LayoutService is provided in the module. If it is not injected, the subscription will never fire.
(Reference)
| # | Gotcha | Why It Matters | What To Do Instead |
|---|---|---|---|
| 1 | ngOnDestroy sets alive = true instead of false |
ECommerceProgressSectionComponent has this bug. The subscription is never cleaned up, causing a memory leak. |
Always set this.alive = false in ngOnDestroy. Double-check every component that uses the alive pattern. |
| 2 | Data fetching in the constructor | Several components (e.g., TrafficComponent, ElectricityComponent) call service methods in the constructor. This makes unit testing harder and can cause issues with Angular's lifecycle. |
Move data fetching to ngOnInit. |
| 3 | No error handler on .subscribe() |
The vast majority of subscriptions in this codebase have no error callback. API failures are silently swallowed, leaving the UI blank with no user feedback. | Always add { error: err => console.error(err) } at minimum. In production, report to an error tracking service. |
| 4 | forkJoin with deprecated multi-argument syntax |
forkJoin(obs1, obs2) (multiple arguments) was removed in RxJS 7. Several components use this pattern. |
Use forkJoin([obs1, obs2]) (array syntax) for all new code. |
| 5 | echartsIntance typo |
Multiple chart components spell the property echartsIntance (missing 's'). This is a consistent typo across the codebase — do not "fix" it in existing files without updating all references. |
Use echartsInstance (correct spelling) in new components. |
| 6 | takeWhile vs takeUntil |
takeWhile(() => this.alive) evaluates the predicate on every emission. If the component is destroyed between emissions, one extra emission may be processed. |
Prefer takeUntil(this.destroy$) for new code — it completes immediately on destroy. |
| 7 | Hardcoded maxValue="20" in CountryOrdersComponent |
The chart's y-axis maximum is hardcoded to 20. If any country's data exceeds 20, the chart clips the data silently. | Derive maxValue dynamically from the actual data range. |
| 8 | Traffic dropdown has no effect on data |
The TrafficComponent period dropdown updates type but never passes it to the data service. The chart always shows the same data regardless of selection. |
Wire the type value to the service call: this.service.getTrafficChartData(this.type). |
| 9 | CKEditor 4 is end-of-life | The CKEditor integration uses version 4.6.2 (2017), which reached end-of-life in June 2023. It is loaded from a CDN with a protocol-relative URL (//cdn.ckeditor.com/...) that will fail on HTTPS pages. |
Upgrade to CKEditor 5 or replace with TinyMCE. At minimum, change the URL to https://. |
| 10 | CountryOrdersMapComponent null reference on invalid countryId |
If countryId does not match any GeoJSON feature, selectFeature(null) is called, which throws TypeError: Cannot read properties of null when accessing featureLayer.feature.properties.name. |
Add a null guard before emitting: if (featureLayer) { this.selectEvent.emit(...); }. |
| 11 | ChatService.reply() mutates shared botReplies array |
The bot reply logic directly mutates the module-level botReplies constant. Repeated calls permanently alter the source data for the session. |
Deep-clone the reply object before mutating: const reply = JSON.parse(JSON.stringify(botReply.reply)). |
| 12 | window:mousemove HostListener fires globally |
TemperatureDraggerComponent listens to window:mousemove for the entire lifetime of the component, even when the user is not dragging. This fires on every mouse movement across the entire page. |
Add/remove the listener dynamically only while isMouseDown is true, using Renderer2. |
(Tutorial)
This tutorial walks you through adding a new read-only data display page to the dashboard section. It exercises the most common patterns in the codebase: the abstract data service, the data-loading component, and the sidebar navigation.
Add a new dashboard page called "Energy Summary" at /pages/dashboard/energy-summary. The page displays a list of energy readings fetched from a mock data service, rendered inside a Nebular card.
ngOnInit and takeUntilpages-menu.tsStep 1: Create the data service interface and abstract class.
Create src/app/@core/data/energy-summary.ts:
import { Observable } from 'rxjs';
export interface EnergyReading {
source: string;
value: number;
unit: string;
}
export abstract class EnergySummaryData {
abstract getReadings(): Observable<EnergyReading[]>;
}Expected result: The file compiles without errors.
Step 2: Create the mock implementation.
Create src/app/@core/mock/energy-summary.service.ts:
import { Injectable } from '@angular/core';
import { of } from 'rxjs';
import { EnergySummaryData, EnergyReading } from '../data/energy-summary';
@Injectable()
export class EnergySummaryMockService extends EnergySummaryData {
private readings: EnergyReading[] = [
{ source: 'Solar', value: 6.4, unit: 'kWh' },
{ source: 'Grid', value: 12.1, unit: 'kWh' },
{ source: 'Battery', value: 3.2, unit: 'kWh' },
];
getReadings() {
return of(this.readings);
}
}Step 3: Register the provider in the core module.
Open src/app/@core/core.module.ts (or the equivalent module that registers mock providers). Add to the providers array:
{ provide: EnergySummaryData, useClass: EnergySummaryMockService }[Not documented — WHO: Team Lead; WHAT: The exact file path and array name where mock providers are registered; WHERE: Update step 3 with the actual file path.]
Step 4: Generate the page component.
ng generate component pages/dashboard/energy-summaryExpected result: Angular CLI creates the component files and registers the component in DashboardModule (or prompts you to specify a module).
Step 5: Implement the component.
Open src/app/pages/dashboard/energy-summary/energy-summary.component.ts and replace its contents:
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { EnergySummaryData, EnergyReading } from '../../../@core/data/energy-summary';
@Component({
selector: 'ngx-energy-summary',
templateUrl: './energy-summary.component.html',
})
export class EnergySummaryComponent implements OnInit, OnDestroy {
readings: EnergyReading[];
private destroy$ = new Subject<void>();
constructor(private energyService: EnergySummaryData) {}
ngOnInit() {
this.energyService.getReadings()
.pipe(takeUntil(this.destroy$))
.subscribe({
next: readings => { this.readings = readings; },
error: err => { console.error('Failed to load energy readings:', err); },
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}Step 6: Write the template.
Open src/app/pages/dashboard/energy-summary/energy-summary.component.html:
<nb-card>
<nb-card-header>Energy Summary</nb-card-header>
<nb-card-body>
<div *ngIf="readings; else loading">
<div *ngFor="let reading of readings" class="reading-row">
<span class="source">{{ reading.source }}</span>
<span class="value">{{ reading.value }} {{ reading.unit }}</span>
</div>
</div>
<ng-template #loading>
<p>Loading...</p>
</ng-template>
</nb-card-body>
</nb-card>Step 7: Add the route.
Open the dashboard routing module (find the file that defines routes for /dashboard). Add:
{ path: 'energy-summary', component: EnergySummaryComponent }[Not documented — WHO: Team Lead; WHAT: The file path of the dashboard routing module; WHERE: Update step 7 with the actual file path.]
Step 8: Add the sidebar entry.
Open src/app/pages/pages-menu.ts and add to the dashboard section:
{
title: 'Energy Summary',
icon: 'flash-outline',
link: '/pages/dashboard/energy-summary',
}ng serve if it is not already running.http://localhost:4200.Once this task is complete, try these progressively more complex tasks:
period parameter to getReadings(period) and re-fetches data on change.EnergySummaryHttpService that calls a real API endpoint and register it as the provider in environment.prod.ts.Angular — The TypeScript-based web application framework used to build this application. Provides the component model, dependency injection system, router, and reactive forms.
Nebular (@nebular/theme) — An open-source Angular UI component library built on the Eva Design System. Provides all UI chrome in this application: cards (nb-card), buttons (nbButton), menus (nb-menu), dialogs (NbDialogService), icons (nb-icon), tabs (nb-tabset), inputs (nbInput), and the theming system (NbThemeService).
Apache ECharts — An open-source JavaScript charting library that renders interactive charts on HTML5 canvas. Used as the primary charting engine in this application.
ngx-echarts — An Angular directive wrapper for Apache ECharts. Provides the echarts attribute directive, [options] binding, and (chartInit) event output.
Chart.js — A second charting library used in the /charts/chartjs screen. Typically integrated via the ng2-charts Angular wrapper.
D3.js — A low-level JavaScript library for producing SVG-based data visualizations. Used in the /charts/d3 screen.
Leaflet — An open-source JavaScript library for interactive maps. Integrated via @asymmetrik/ngx-leaflet.
@asymmetrik/ngx-leaflet — Angular directive wrapper for Leaflet. Provides the leaflet attribute directive, [leafletOptions], [leafletLayers], and (leafletMapReady) bindings.
ng2-smart-table — An Angular data grid library providing <ng2-smart-table> with built-in sorting, filtering, pagination, and inline CRUD. Uses LocalDataSource for in-memory data and ServerDataSource for server-side data.
LocalDataSource — A class from ng2-smart-table that stores table data in memory and handles sorting, filtering, and pagination client-side.
TinyMCE — A WYSIWYG rich text editor loaded from CDN. Integrated via a custom Angular wrapper component (ngx-tiny-mce).
CKEditor 4 — A second WYSIWYG rich text editor loaded from CDN. End-of-life as of June 2023.
RxJS — The reactive programming library bundled with Angular. Provides Observable, Subject, forkJoin, takeWhile, takeUntil, delay, debounceTime, and other operators used throughout the application.
NbThemeService — Nebular's theme management service. Provides getJsTheme() (returns an Observable of the current theme's JavaScript variable map) and onMediaQueryChange() (returns an Observable that emits when the viewport crosses a breakpoint).
NbDialogService — Nebular's service for opening modal dialog overlays programmatically. Returns an NbDialogRef with an onClose observable.
NbDialogRef<T> — A reference object returned by NbDialogService.open(). Provides close(result?) to dismiss the dialog and onClose to receive the result.
NbWindowService — Nebular's service for opening floating, draggable window overlays.
NbToastrService — Nebular's service for displaying non-blocking toast notification overlays.
NbMediaBreakpointsService — Nebular service that provides the application's configured responsive breakpoint map (name → pixel width).
NbTreeGridDataSourceBuilder — Nebular service that constructs an NbTreeGridDataSource from a TreeNode[] array for use with nb-tree-grid.
LayoutService — An internal application utility service (src/app/@core/utils/layout.service.ts) that broadcasts layout size change events via an RxJS Subject. Chart components subscribe to onSafeChangeLayoutSize() (debounced 350ms) to trigger echartsInstance.resize().
Eva Icons — An open-source SVG icon pack used by Nebular. Icons are referenced by name (e.g., arrow-ios-downward, file-text-outline) via the nb-icon component.
Ionicons — A CSS icon font library used for social media icons in the Footer component and for the search icon in SearchInputComponent.
Shell Component — A component whose class body is empty and whose template contains only <router-outlet>. Exists solely to provide a URL segment and a rendering target for child routes.
Abstract Data Service Pattern — An Angular DI pattern where a domain's data contract is defined as an abstract class in src/app/@core/data/. A concrete mock implementation is registered in the module. Components inject the abstract class, never the concrete implementation.
alive flag pattern — A subscription lifecycle management pattern using a private alive: boolean = true property with takeWhile(() => this.alive) on subscriptions and this.alive = false in ngOnDestroy. Superseded by the destroy$ subject pattern in modern Angular.
destroy$ subject pattern — A subscription lifecycle management pattern using private destroy$ = new Subject<void>() with takeUntil(this.destroy$) on subscriptions and this.destroy$.next(); this.destroy$.complete() in ngOnDestroy. Preferred over the alive flag pattern.
forkJoin — An RxJS operator that subscribes to multiple observables simultaneously and emits a single array of their last values only after all source observables have completed. Used to load multiple datasets in parallel before rendering.
takeWhile — An RxJS operator that completes a subscription when a predicate function returns false. Used with () => this.alive for subscription cleanup.
takeUntil — An RxJS operator that completes a subscription when a notifier observable emits. Used with this.destroy$ for subscription cleanup.
ngx- prefix — The component selector namespace convention used for all custom application components in this codebase (e.g., ngx-dashboard, ngx-electricity-chart). Distinguishes application components from Nebular (nb-) components.
nb- prefix — The component selector namespace for Nebular UI library components (e.g., nb-card, nb-button, nb-icon).
Partial ECharts update — Calling echartsInstance.setOption({ series: [...] }) with only the changed portions of the configuration, rather than replacing the entire options object. More performant than full re-initialization.
delay(1) pattern — Applying delay(1) to the getJsTheme() subscription in ngAfterViewInit to defer chart initialization by one event loop tick, preventing ExpressionChangedAfterItHasBeenCheckedError.
getUrlPath(id) pattern — A method pattern used in SVG-heavy components to construct absolute url(...) references for SVG clipPath and filter elements, ensuring they resolve correctly when the app is deployed at a non-root base href.
confirmDelete: true — A ng2-smart-table settings flag that causes the table to emit a (deleteConfirm) event instead of immediately deleting a row, allowing the application to intercept and confirm the action.
NbMediaBreakpoint — A Nebular object with name: string and width: number properties representing a single responsive breakpoint (e.g., { name: 'md', width: 768 }).
NbComponentStatus — A Nebular type union for semantic color roles: 'primary', 'success', 'info', 'warning', 'danger', 'basic', 'control'.
NbComponentSize — A Nebular type union for component size scale: 'tiny', 'small', 'medium', 'large', 'giant'.
NbComponentShape — A Nebular type union for border-radius style: 'rectangle', 'semi-round', 'round'.
NbGlobalPosition — A union type encompassing NbGlobalPhysicalPosition (e.g., TOP_RIGHT) and NbGlobalLogicalPosition (e.g., TOP_END) for positioning toast notifications.
TreeNode<T> — A generic interface for hierarchical tree data used by nb-tree-grid. Each node has a data: T payload and an optional children: TreeNode<T>[] array.
FSEntry — The domain model for a file system entry in the Tree Grid screen: { name, size, kind, items? }. The kind field distinguishes directories ('dir') from files.
PositionModel — A data class representing a geographic coordinate: { lat: number, lng: number }. Defaults to Minsk, Belarus (53.9, 27.5667).
Camera — A domain interface representing a security camera: { title: string, source: string }. The source field is a video stream URL.
TrafficList — An interface representing a single-period traffic summary: { date, value, delta: { up: boolean, value: number }, comparison: { prevDate, prevValue, nextDate, nextValue } }.
TrafficBar — An interface representing traffic as a time-series bar chart: { data: number[], labels: string[], formatter: string }.
ProfitChart — An interface for profit breakdown chart data: { chartLabel: string[], data: number[][] }. The data array contains three sub-arrays for Canceled, Payment, and All Orders series.
OrdersChart — An interface for orders time-series chart data: { chartLabel: string[], linesData: number[][] }.
OrderProfitChartSummary — An interface for a KPI summary item: { title: string, value: number }.
Electricity — A data model for an electricity usage category with monthly breakdown: { title, active?, months: Month[] }.
Month — A sub-model within Electricity for one month's data: { month, delta, down: boolean, kWatts, cost }.
ElectricityChart — A simplified label/value pair for chart rendering: { label: string, value: number }.
Temperature (interface) — A sensor reading model: { value: number, min: number, max: number }. Used for both temperature and humidity data.
Contacts — An interface for a contact list entry: { user: User, type: string }.
RecentUsers — An interface extending Contacts with a time field (numeric timestamp).
User — A minimal user profile: { name: string, picture: string }.
NgxLegendItemColor — An enum defining valid legend icon colors: GREEN, PURPLE, LIGHT_PURPLE, BLUE, YELLOW.
ProgressInfo — An interface for a progress bar metric: { title, value, activeProgress, description }.
UserActive — An interface for a user activity record: { date, pagesVisitCount, deltaUp: boolean, newVisits }.
OutlineData — A labeled data point for line charts: { label: string, value: number }.
VIEW_BOX_SIZE — The constant 300 used as the SVG coordinate system reference width in TemperatureDraggerComponent. All geometry is calculated in these units and scaled to actual pixel dimensions.
countryOrders theme variables — A named group within Nebular's JS theme variables providing color tokens for the Country Orders map and chart (e.g., countryFillColor, chartGradientFrom).
earningPie theme variables — A named group within Nebular's JS theme variables providing color tokens for the Earning Card pie chart (e.g., firstPieGradientLeft, center, radius).
visitorsPie theme variables — A named group within Nebular's JS theme variables providing color tokens for the Visitors Statistics pie chart.
solar theme variables — A named group within Nebular's JS theme variables providing color tokens for the Solar energy donut chart.
electricity theme variables — A named group within Nebular's JS theme variables providing color tokens for the Electricity line chart.
orders theme variables — A named group within Nebular's JS theme variables providing color tokens for the Orders area chart.
profit theme variables — A named group within Nebular's JS theme variables providing color tokens for the Profit bar chart.
trafficBarEchart theme variables — A named group within Nebular's JS theme variables providing color tokens for the Traffic Bar chart.