Charts & plugins

Heavy third-party libraries are wrapped in thin components that lazy-load their dependency only on the browser, only when the component is used. Install the peers you need; skip the ones you don't.

How lazy loading works

The plugin wrappers (LteApexChart, LteDatatable, LteInputFlatpickr, LteInputTomSelect) declare their library as an optional peer dependency and import it with a dynamic import() guarded by a platform check. So the library never bloats your main bundle, never runs on the server during SSR/prerender, and degrades gracefully (a static table, a plain input) if the peer isn't installed — logging a single console warning. Install only what you use:

npm install apexcharts flatpickr tom-select simple-datatables

ApexCharts

<lte-apex-chart> takes a raw ApexCharts options object via its required options input. Pass any valid ApexCharts config (series, chart, xaxis, …); when the input changes, the chart re-renders, and it is destroyed on teardown.

import { Component, computed, signal } from '@angular/core';
import { AppContentComponent, CardComponent, ApexChartComponent } from '@adminlte/angular';

@Component({
  selector: 'app-dashboard',
  imports: [AppContentComponent, CardComponent, ApexChartComponent],
  template: `
    <lte-app-content title="Dashboard">
      <lte-card title="Sales">
        <lte-apex-chart [options]="chartOptions()" />
      </lte-card>
    </lte-app-content>
  `,
})
export class DashboardPage {
  private readonly series = signal([30, 40, 45, 50, 49, 60, 70]);

  readonly chartOptions = computed(() => ({
    chart: { type: 'area', height: 300, toolbar: { show: false } },
    series: [{ name: 'Revenue', data: this.series() }],
    xaxis: { categories: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] },
  }));
}

Build options as a computed() so the chart reacts to your signals automatically. apexcharts is an optional peer — install it to activate the wrapper.

Datatables

<lte-datatable> renders a plain Bootstrap table from columns + data, then progressively enhances it with simple-datatables (search, sort, paginate). Columns are DatatableColumn{ key, label?, sortable? }.

<lte-datatable
  [columns]="[
    { key: 'name', label: 'Name' },
    { key: 'role', label: 'Role' },
    { key: 'joined', label: 'Joined', sortable: false }
  ]"
  [data]="rows"
  [options]="{ perPage: 10 }"
/>

Flatpickr & Tom Select

Two form controls wrap popular pickers. Both are ControlValueAccessors with a [(value)] model, and accept extra library settings.

<lte-input-flatpickr
  label="Event date"
  [options]="{ dateFormat: 'Y-m-d' }"
  [(value)]="date"
/>

<lte-input-tom-select
  label="Tags"
  [multiple]="true"
  [options]="[{ value: 'ng', text: 'Angular' }, { value: 'ts', text: 'TypeScript' }]"
  [(value)]="tags"
/>

<lte-input-flatpickr> forwards extra flatpickr options through [options]; <lte-input-tom-select> forwards Tom Select settings through [settings] and takes its choices as TomSelectOption[].

Plugin-heavy demo pages

Some demo pages use libraries that have no first-class wrapper (FullCalendar, Quill, SortableJS, jsVectorMap). The pattern there is to load the library inside Angular's afterNextRender using dynamic imports, so SSR/prerender never touches the browser-only code, and to wrap the whole init in try/catch so a failed load degrades to an empty card instead of breaking the page:

import { Component, ElementRef, afterNextRender, viewChild } from '@angular/core';

@Component({ /* ... */ })
export class CalendarPage {
  private readonly calendarEl = viewChild.required<ElementRef>('calendar');

  constructor() {
    afterNextRender(async () => {
      try {
        const { Calendar } = await import('@fullcalendar/core');
        const dayGrid = (await import('@fullcalendar/daygrid')).default;
        const calendar = new Calendar(this.calendarEl().nativeElement, {
          plugins: [dayGrid],
          initialView: 'dayGridMonth',
        });
        calendar.render();
      } catch {
        // optional progressive enhancement — leave the card empty on failure
      }
    });
  }
}

The calendar, kanban (SortableJS) and editor (Quill) demo pages all follow this afterNextRender + dynamic-import recipe. Use it whenever you integrate a browser-only library that the library doesn't wrap for you.


AdminLTE 4 · Angular port Edit on GitHub