Angular Material themes toggle 不切换主题

Angular Material themes toggle not switching themes

场景
我们遵循此 tutorial 为我们的用户提供深色和浅色主题。

问题
浏览器加载预期的 css-File(深色或浅色)。尽管如此,这些样式并未应用于我们的组件。 这发生在 <head></head>:

<link rel="stylesheet" type="text/css" href="theme-dark.css">

<link rel="stylesheet" type="text/css" href="theme-light.css">

如您所见,浏览器正确切换了两个主题。

代码
angular.json

"styles": [
          "projects/menu/src/styles.scss",
          {
            "input": "projects/menu/src/theming/theme-dark.scss",
            "bundleName": "theme-dark",
            "inject": false
          },
          {
            "input": "projects/menu/src/theming/theme-light.scss",
            "bundleName": "theme-light",
            "inject": false
          }
        ],

theme-service.ts

private _mainTheme$: BehaviorSubject<string> = new BehaviorSubject(
    'theme-light'
);
private _darkMode$: BehaviorSubject<boolean> = new BehaviorSubject(false);

darkMode$: Observable<boolean> = this._darkMode$.asObservable();

private _renderer: Renderer2;
private head: HTMLElement;
private themeLinks: HTMLElement[] = [];

theme$: Observable<[string, boolean]>;

constructor(
    rendererFactory: RendererFactory2,
    @Inject(DOCUMENT) document: Document
) {
    this.head = document.head;
    this._renderer = rendererFactory.createRenderer(null, null);
    this.theme$ = combineLatest([this._mainTheme$, this._darkMode$]);
    this.theme$.subscribe(async ([mainTheme, darkMode]) => {
        const cssExt = '.css';
        const cssFilename = darkMode
            ? 'theme-dark' + cssExt
            : mainTheme + cssExt;
        await this.loadCss(cssFilename);
        if (this.themeLinks.length == 2)
            this._renderer.removeChild(this.head, this.themeLinks.shift());
    });
}

setMainTheme(name: string) {
    this._mainTheme$.next(name);
}

setDarkMode(value: boolean) {
    this._darkMode$.next(value);
}

private async loadCss(filename: string) {
    return new Promise((resolve) => {
        const linkEl: HTMLElement = this._renderer.createElement('link');
        this._renderer.setAttribute(linkEl, 'rel', 'stylesheet');
        this._renderer.setAttribute(linkEl, 'type', 'text/css');
        this._renderer.setAttribute(linkEl, 'href', filename);
        this._renderer.setProperty(linkEl, 'onload', resolve);
        this._renderer.appendChild(this.head, linkEl);
        this.themeLinks = [...this.themeLinks, linkEl];
    });
}

theme-base.scss

@import '~@angular/material/theming';

@include mat-core();

@mixin theming($theme) {
  @include angular-material-theme($theme);
}

theme-dark.scss

@import '~@angular/material/theming';
@import './theme-base';

@include mat-core();

// color palette definitions

$primary: mat-palette($dark-md-mcgpaletteblack);
$accent: mat-palette($md-mcgpalettewhite);
$warn: mat-palette($dark-md-mcgpalettered);
$background:mat-palette($dark-md-mcgpalettebackground);
$confirm: mat-palette($dark-md-mcgpaletteorange);

$theme-dark: mat-dark-theme((color: (primary: $primary,
  accent: $accent,
  warn: $warn,
  background: $background,
  confirm: $confirm)));

theme-dark.scss - 看起来一样

style.scss 中我们导入两个主题:

styles.scss

@import 'projects/menu/src/theming/theme-dark.scss';
@import 'projects/menu/src/theming/theme-light.scss';

.theme-dark {
  @include angular-material-theme($theme-dark)
}

.theme-light {
  @include angular-material-theme($theme-light)
}

然后我们使用这些变量:

styles.scss

body {
  background: mat-color($background, default);
}

h1 {
   color: mat-color($accent, default);
}

但目前我们也在我们的组件中导入它们。我认为这是一回事,那是错误的。
对话框如下所示:

@import 'projects/menu/src/theming/theme-dark.scss';
@import 'projects/menu/src/theming/theme-light.scss';

.theme-dark {
  @include angular-material-theme($theme-dark)
}

.theme-light{
  @include angular-material-theme($theme-light)
}

.mat-button {
  color: mat-color($accent, default);
  background-color: mat-color($confirm, default);
}

我试过的:

我们不知道如何将缺失的部分组合起来。我们需要使用 mixins 吗?或者我们是否缺少在根组件上设置 class dark-theme

非常感谢您。

我们终于成功了。

我们的错误:

  • @include mat-core(); 只能导入一次(在 theme-base.scss 中)
  • @import 'projects/menu/src/theming/theme-dark.scss';@import 'projects/menu/src/theming/theme-light.scss'; 只能导入一次(在 styles.scss 中)。在所有组件中导入它们可以让包大小爆炸。
  • 直接在theme-dark.scss
  • 中设置theme-darkclass
  • 不要在 styles.scss 或任何其他组件主题中设置 classes theme-darktheme-light
  • 为整个应用程序创建两个包装元素:<div [ngClass]="{ 'theme-dark': darkMode$ | async }"> <div class="mat-app-background"> <router-outlet></router-outlet></div></div>
  • 如果组件需要访问主题,使用 mixins(示例如下)
  • theme-service.ts 中,使用 OverlayContainer 也可以将样式应用于 material 对话框(如 所述)

混合示例:

settings-dialog.component.scss

@import '~@angular/material/theming';

@mixin settings-dialog-theme($theme) {
  .settings-dialog {
    @include mat-elevation(2);
    .mat-button {
      background-color: mat-color($confirm);
    }
  }
}

settings-dialog.component.html

<div mat-dialog-content class="settings-dialog"> Put your content here </div>

按照描述创建组件主题 here
component-themes.scss

@import 'your-path/settings-dialog.component';
@mixin component-themes($theme) {
  @include settings-dialog-theme($theme);
}

将其导入到您的基本主题文件中
theme-base.scss

@import '~@angular/material/theming';
@import './component-themes.scss';

@include mat-core();

@mixin theming($theme) {
    @include angular-material-theme($theme);
    @include component-themes($theme);
}