CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ng2-charts

Reactive, responsive, beautiful charts for Angular based on Chart.js

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

theming.mddocs/

Dynamic Theming

The ThemeService provides dynamic theming capabilities for ng2-charts, allowing applications to change chart colors and styling at runtime based on user preferences or application state.

Capabilities

ThemeService

Injectable service that manages dynamic theme options for all charts in the application.

/**
 * Service for managing dynamic chart themes
 * Allows runtime updates to chart styling and colors
 */
@Injectable({
  providedIn: 'root'
})
export class ThemeService {
  /** 
   * Observable stream of current theme options
   * Charts automatically update when this changes
   */
  colorschemesOptions: BehaviorSubject<ChartOptions | undefined>;
}

Theme Configuration

Set theme options that will be applied to all charts.

/**
 * Sets global theme options for all charts
 * Options are merged with individual chart options
 * @param options - Chart.js options object with theme overrides
 */
setColorschemesOptions(options: ChartConfiguration['options']): void;

/**
 * Gets the current theme options
 * @returns Current theme configuration or undefined
 */
getColorschemesOptions(): ChartConfiguration['options'];

Usage Examples:

import { Component, OnInit } from '@angular/core';
import { BaseChartDirective, ThemeService } from 'ng2-charts';
import { ChartOptions, ChartData } from 'chart.js';

@Component({
  selector: 'app-theme-demo',
  template: `
    <div class="theme-controls">
      <button (click)="setLightTheme()">Light Theme</button>
      <button (click)="setDarkTheme()">Dark Theme</button>
      <button (click)="setCustomTheme()">Custom Theme</button>
    </div>
    
    <canvas baseChart [data]="chartData" [type]="'bar'"></canvas>
  `,
  standalone: true,
  imports: [BaseChartDirective]
})
export class ThemeDemoComponent implements OnInit {
  
  constructor(private themeService: ThemeService) {}
  
  chartData: ChartData = {
    labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
    datasets: [{
      label: 'Sample Data',
      data: [12, 19, 3, 5, 2, 3]
    }]
  };

  setLightTheme() {
    const lightTheme: ChartOptions = {
      plugins: {
        legend: {
          labels: {
            color: '#333333'
          }
        }
      },
      scales: {
        x: {
          ticks: {
            color: '#333333'
          },
          grid: {
            color: 'rgba(0, 0, 0, 0.1)'
          }
        },
        y: {
          ticks: {
            color: '#333333'
          },
          grid: {
            color: 'rgba(0, 0, 0, 0.1)'
          }
        }
      }
    };
    
    this.themeService.setColorschemesOptions(lightTheme);
  }

  setDarkTheme() {
    const darkTheme: ChartOptions = {
      plugins: {
        legend: {
          labels: {
            color: '#ffffff'
          }
        }
      },
      scales: {
        x: {
          ticks: {
            color: '#ffffff'
          },
          grid: {
            color: 'rgba(255, 255, 255, 0.1)'
          }
        },
        y: {
          ticks: {
            color: '#ffffff'
          },
          grid: {
            color: 'rgba(255, 255, 255, 0.1)'
          }
        }
      }
    };
    
    this.themeService.setColorschemesOptions(darkTheme);
  }

  setCustomTheme() {
    const customTheme: ChartOptions = {
      plugins: {
        legend: {
          labels: {
            color: '#ff6b6b',
            font: {
              size: 14,
              weight: 'bold'
            }
          }
        }
      },
      scales: {
        x: {
          ticks: {
            color: '#4ecdc4'
          },
          grid: {
            color: 'rgba(78, 205, 196, 0.3)'
          }
        },
        y: {
          ticks: {
            color: '#4ecdc4'
          },
          grid: {
            color: 'rgba(78, 205, 196, 0.3)'
          }
        }
      }
    };
    
    this.themeService.setColorschemesOptions(customTheme);
  }
}

Theme Override Behavior

The ThemeService uses a special merging behavior for theme options:

  • Simple fields: Direct replacement of matching fields in chart options
  • Arrays: Single object in theme array acts as template for all elements in chart array
  • Objects: Deep merge with existing chart options

Array Override Example

// Theme setting that affects ALL axes
const themeOptions: ChartOptions = {
  scales: {
    x: [{  // Single object template
      ticks: { color: 'white' },
      gridLines: { color: 'rgba(255,255,255,0.1)' }
    }],
    y: [{  // Single object template  
      ticks: { color: 'white' },
      gridLines: { color: 'rgba(255,255,255,0.1)' }
    }]
  }
};

// This will apply the styling to ALL x and y axes in ALL charts
themeService.setColorschemesOptions(themeOptions);

Advanced Theming Patterns

Reactive Theme Switching

import { Component } from '@angular/core';
import { BaseChartDirective, ThemeService } from 'ng2-charts';
import { ChartOptions } from 'chart.js';
import { BehaviorSubject } from 'rxjs';

type Theme = 'light' | 'dark' | 'blue';

@Component({
  selector: 'app-reactive-theme',
  template: `
    <select (change)="onThemeChange($event)">
      <option value="light">Light</option>
      <option value="dark">Dark</option>
      <option value="blue">Blue</option>
    </select>
  `,
  standalone: true,
  imports: [BaseChartDirective]
})
export class ReactiveThemeComponent {
  private themeSubject = new BehaviorSubject<Theme>('light');
  
  constructor(private themeService: ThemeService) {
    this.themeSubject.subscribe(theme => {
      this.applyTheme(theme);
    });
  }

  onThemeChange(event: any) {
    this.themeSubject.next(event.target.value as Theme);
  }

  private applyTheme(theme: Theme) {
    const themes = {
      light: {
        plugins: {
          legend: { labels: { color: '#333' } }
        },
        scales: {
          x: { ticks: { color: '#333' } },
          y: { ticks: { color: '#333' } }
        }
      },
      dark: {
        plugins: {
          legend: { labels: { color: '#fff' } }
        },
        scales: {
          x: { ticks: { color: '#fff' } },
          y: { ticks: { color: '#fff' } }
        }
      },
      blue: {
        plugins: {
          legend: { labels: { color: '#2196f3' } }
        },
        scales: {
          x: { ticks: { color: '#2196f3' } },
          y: { ticks: { color: '#2196f3' } }
        }
      }
    };
    
    this.themeService.setColorschemesOptions(themes[theme]);
  }
}

Theme Persistence

import { Injectable } from '@angular/core';
import { ThemeService } from 'ng2-charts';
import { ChartOptions } from 'chart.js';

@Injectable({
  providedIn: 'root'
})
export class PersistentThemeService {
  private readonly THEME_KEY = 'chart-theme';
  
  constructor(private themeService: ThemeService) {
    this.loadSavedTheme();
  }
  
  setTheme(options: ChartOptions) {
    this.themeService.setColorschemesOptions(options);
    localStorage.setItem(this.THEME_KEY, JSON.stringify(options));
  }
  
  private loadSavedTheme() {
    const saved = localStorage.getItem(this.THEME_KEY);
    if (saved) {
      try {
        const theme = JSON.parse(saved);
        this.themeService.setColorschemesOptions(theme);
      } catch (e) {
        console.warn('Failed to load saved theme:', e);
      }
    }
  }
}

CSS Variable Integration

import { Component, OnInit } from '@angular/core';
import { BaseChartDirective, ThemeService } from 'ng2-charts';
import { ChartOptions, ChartData } from 'chart.js';

@Component({
  selector: 'app-css-theme',
  template: `<canvas baseChart [data]="chartData" [type]="'line'"></canvas>`,
  styles: [`
    :host {
      --primary-color: #2196f3;
      --text-color: #333333;
      --grid-color: rgba(0, 0, 0, 0.1);
    }
    
    :host.dark-theme {
      --primary-color: #90caf9;
      --text-color: #ffffff;
      --grid-color: rgba(255, 255, 255, 0.1);
    }
  `],
  standalone: true,
  imports: [BaseChartDirective]
})
export class CssThemeComponent implements OnInit {
  
  chartData: ChartData = {
    labels: ['January', 'February', 'March', 'April', 'May', 'June'],
    datasets: [{
      label: 'Sample Data',
      data: [12, 19, 3, 5, 2, 3]
    }]
  };
  
  constructor(private themeService: ThemeService) {}
  
  ngOnInit() {
    this.updateThemeFromCSS();
  }
  
  private updateThemeFromCSS() {
    const computedStyle = getComputedStyle(document.documentElement);
    const primaryColor = computedStyle.getPropertyValue('--primary-color').trim();
    const textColor = computedStyle.getPropertyValue('--text-color').trim();
    const gridColor = computedStyle.getPropertyValue('--grid-color').trim();
    
    const theme: ChartOptions = {
      plugins: {
        legend: {
          labels: {
            color: textColor
          }
        }
      },
      scales: {
        x: {
          ticks: { color: textColor },
          grid: { color: gridColor }
        },
        y: {
          ticks: { color: textColor },
          grid: { color: gridColor }
        }
      }
    };
    
    this.themeService.setColorschemesOptions(theme);
  }
}

Theme Option Types

interface ThemeOptions extends ChartOptions {
  plugins?: {
    legend?: {
      labels?: {
        color?: string;
        font?: {
          size?: number;
          weight?: string | number;
          family?: string;
        };
      };
    };
    tooltip?: {
      backgroundColor?: string;
      titleColor?: string;
      bodyColor?: string;
      borderColor?: string;
    };
  };
  scales?: {
    [key: string]: {
      ticks?: {
        color?: string;
        font?: {
          size?: number;
          family?: string;
        };
      };
      grid?: {
        color?: string;
        borderColor?: string;
      };
    };
  };
}

docs

chart-directive.md

configuration.md

index.md

theming.md

tile.json