Angular v5.x中如何动态获取Angular Material自定义主题列表?
Hey there! Let's figure out how to dynamically pull in all your custom Angular Material themes without hardcoding their names—perfect for populating that dropdown menu. Since you're working with Angular v5.x, I’ll focus on solutions that play nice with this specific version:
If your themes are applied via CSS classes (like theme-dark, theme-ocean), you can scan the app's loaded stylesheets on initialization to pick up these class names automatically.
Step 1: Add Theme Detection Logic to Your Component
In your root component (e.g., app.component.ts), add code to parse stylesheets and extract theme classes:
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { availableThemes: string[] = []; selectedTheme: string = 'theme-light'; // Default theme ngOnInit() { this.extractThemesFromStyles(); // Set initial theme if needed document.body.classList.add(this.selectedTheme); } private extractThemesFromStyles(): void { // Adjust this regex to match your theme class pattern const themeClassRegex = /theme-\w+/g; const allStylesheets = Array.from(document.styleSheets); allStylesheets.forEach(sheet => { try { const cssRules = Array.from(sheet.cssRules || sheet.rules); cssRules.forEach(rule => { if ('selectorText' in rule) { const matches = (rule as CSSStyleRule).selectorText.match(themeClassRegex); if (matches) { matches.forEach(match => { // Remove the leading dot from the CSS selector const className = match.replace('.', ''); if (!this.availableThemes.includes(className)) { this.availableThemes.push(className); } }); } } }); } catch (err) { // Skip cross-origin stylesheets (like external CDNs) console.warn(`Skipping inaccessible stylesheet: ${sheet.href}`); } }); } switchTheme(themeName: string): void { // Remove existing theme classes document.body.classList.forEach(cls => { if (cls.startsWith('theme-')) document.body.classList.remove(cls); }); // Apply new theme document.body.classList.add(themeName); this.selectedTheme = themeName; } }
Step 2: Update Your Template for the Dropdown
Use the availableThemes array to populate your Material select:
<mat-select [(value)]="selectedTheme" (selectionChange)="switchTheme($event.value)"> <mat-option *ngFor="let theme of availableThemes" [value]="theme"> {{ theme.replace('theme-', '') | titlecase }} </mat-option> </mat-select>
For a more stable approach, generate a static list of themes during your build process. This avoids DOM parsing and works great if your themes are stored in a consistent directory.
Step 1: Create a Node Script to Generate Theme List
Make a file named generate-theme-list.js in your project root:
const fs = require('fs'); const path = require('path'); // Path to your theme files (adjust this to match your project structure) const themesDir = path.join(__dirname, 'src/styles/themes'); // Output file that will hold your theme list const outputFile = path.join(__dirname, 'src/app/theme-list.ts'); // Get all SCSS theme files const themeFiles = fs.readdirSync(themesDir).filter(file => file.endsWith('.scss')); // Extract theme names (remove .scss extension, add 'theme-' prefix if missing) const themeNames = themeFiles.map(file => { const baseName = file.replace('.scss', ''); return baseName.startsWith('theme-') ? baseName : `theme-${baseName}`; }); // Write the theme list to a TypeScript file const tsContent = `export const AVAILABLE_THEMES = ${JSON.stringify(themeNames)} as const;`; fs.writeFileSync(outputFile, tsContent); console.log('Successfully generated theme list!');
Step 2: Add Script to Your Build Workflow
Update your package.json to run this script before starting or building the app:
"scripts": { "prestart": "node generate-theme-list.js", "prebuild": "node generate-theme-list.js", // ... your other scripts }
Step 3: Use the Generated Theme List in Your Component
Import the theme list and use it directly:
import { Component, OnInit } from '@angular/core'; import { AVAILABLE_THEMES } from './theme-list'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { availableThemes = AVAILABLE_THEMES; selectedTheme: typeof AVAILABLE_THEMES[number] = 'theme-light'; ngOnInit() { document.body.classList.add(this.selectedTheme); } switchTheme(themeName: typeof AVAILABLE_THEMES[number]): void { document.body.classList.forEach(cls => { if (cls.startsWith('theme-')) document.body.classList.remove(cls); }); document.body.classList.add(themeName); this.selectedTheme = themeName; } }
- Solution 1 is great if you need to handle dynamically loaded themes (e.g., themes added after the app initializes).
- Solution 2 is more reliable for static themes, as it avoids DOM parsing edge cases and gives you type safety in TypeScript.
内容的提问来源于stack exchange,提问作者Rick

