【问题标题】:Switch themes in angular material 5在角度材料 5 中切换主题
【发布时间】:2019-04-08 22:17:26
【问题描述】:

我一直在阅读一些关于此的文章,但它们似乎以几种不同的方式相互冲突。我希望为最新版本的角材料 [5.0.0-rc0] 重新创建与 angular material documentation site 相同的主题切换

我有两个自定义主题,一个是 custom-theme.scss,另一个是 light-custom-theme.scss,几乎相同,没有 mat-dark-theme

@import '~@angular/material/theming';
$custom-theme-primary: mat-palette($mat-blue);
$custom-theme-accent: mat-palette($mat-orange, A200, A100, A400);
$custom-theme-warn: mat-palette($mat-red);
$custom-theme: mat-dark-theme($custom-theme-primary, $custom-theme-accent, $custom-theme-warn);

@include angular-material-theme($custom-theme);

我的 styles.scss 看起来像这样

@import '~@angular/material/theming';
@include mat-core();
@import 'custom-theme.scss';
@import 'light-custom-theme.scss';
.custom-theme {
  @include angular-material-theme($custom-theme);
}

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

然后在index.html中调用<body class="mat-app-background">

当我做一个主题时,一切都很好。但我试图在两者之间切换。将两个主题添加到 angular-cli.json 中,light-custom-theme 接管

"styles": [
  "styles.scss",
  "custom-theme.scss",
  "light-custom-theme.scss"
],

我的一个组件中有以下代码来处理切换主题

toggleTheme(): void {
  if (this.overlay.classList.contains("custom-theme")) {
    this.overlay.classList.remove("custom-theme");
    this.overlay.classList.add("light-custom-theme");
  } else if (this.overlay.classList.contains("light-custom-theme")) {
    this.overlay.classList.remove("light-custom-theme");
    this.overlay.classList.add("custom-theme");
  } else {
    this.overlay.classList.add("light-custom-theme");
  }
}

但无论何时运行,主题都保持不变。值得一提的是,overlay.classList 中的位置 0 处已经有一个“cdk-overlay-container”对象

0:"cdk-overlay-container"
1:"custom-theme"
length:2
value:"cdk-overlay-container custom-theme" 

我不确定如何调试它,因为角度材料文档并没有给我太多的工作,任何帮助都将不胜感激!

谢谢!

【问题讨论】:

    标签: angular sass angular-material angular5


    【解决方案1】:

    这是 Angular 5.1+/Angular Material 5.0+ 的替代解决方案。

    *编辑:如前所述,这仍然适用于 Angular 7+。

    工作可编辑示例 - https://stackblitz.com/edit/dynamic-material-theming

    在 theme.scss 中,包含一个默认主题(注意它没有保存在类名下 - 这样 Angular 将使用它作为默认主题),然后是一个明暗主题。

    theme.scss

    @import '~@angular/material/theming';
    @include mat-core();
    
    // Typography
    $custom-typography: mat-typography-config(
      $font-family: Raleway,
      $headline: mat-typography-level(24px, 48px, 400),
      $body-1: mat-typography-level(16px, 24px, 400)
    );
    @include angular-material-typography($custom-typography);
    
    // Default colors
    $my-app-primary: mat-palette($mat-teal, 700, 100, 800);
    $my-app-accent:  mat-palette($mat-teal, 700, 100, 800);
    
    $my-app-theme: mat-light-theme($my-app-primary, $my-app-accent);
    @include angular-material-theme($my-app-theme);
    
    // Dark theme
    $dark-primary: mat-palette($mat-blue-grey);
    $dark-accent:  mat-palette($mat-amber, A200, A100, A400);
    $dark-warn:    mat-palette($mat-deep-orange);
    
    $dark-theme:   mat-dark-theme($dark-primary, $dark-accent, $dark-warn);
    
    .dark-theme {
      @include angular-material-theme($dark-theme);
    }
    
    // Light theme
    $light-primary: mat-palette($mat-grey, 200, 500, 300);
    $light-accent: mat-palette($mat-brown, 200);
    $light-warn: mat-palette($mat-deep-orange, 200);
    
    $light-theme: mat-light-theme($light-primary, $light-accent, $light-warn);
    
    .light-theme {
      @include angular-material-theme($light-theme)
    }
    

    在 app.component 文件中,包含来自 @angular/cdk/overlay 的 OverlayContainer。你可以在这里找到 Angular 的文档https://material.angular.io/guide/theming;虽然他们的实现有点不同。请注意,我还必须在 app.module 中包含 OverlayModule 作为导入。

    在我的 app.component 文件中,我还将@HostBinding('class') componentCssClass; 声明为一个变量,用于将主题设置为一个类。

    app.component.ts

    import {Component, HostBinding } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    import { OverlayContainer} from '@angular/cdk/overlay';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css'],
    })
    export class AppComponent {
    
      constructor(public overlayContainer: OverlayContainer) {}
    
      @HostBinding('class') componentCssClass;
    
      onSetTheme(theme) {
        this.overlayContainer.getContainerElement().classList.add(theme);
        this.componentCssClass = theme;
      }
    
    }
    

    app.module.ts

    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    
    import { HttpClientModule } from '@angular/common/http';
    
    import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
    import { MatCardModule } from '@angular/material/card';
    import { MatButtonModule } from '@angular/material/button';
    
    import { AppComponent } from './app.component';
    
    import { OverlayModule} from '@angular/cdk/overlay';
    
    @NgModule({
      declarations: [
        AppComponent,
      ],
      imports: [
        BrowserModule,
        HttpClientModule,
        BrowserAnimationsModule,
        MatCardModule,
        MatButtonModule,
        OverlayModule
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule {}
    

    最后,从你的视图中调用 onSetTheme 函数。

    app.component.html

    <button mat-raised-button color="primary" (click)="onSetTheme('default-theme')">Default</button>
    <button mat-raised-button color="primary" (click)="onSetTheme('dark-theme')">Dark</button>
    <button mat-raised-button color="primary" (click)="onSetTheme('light-theme')">Light</button>
    

    您可以考虑使用 observable,以便功能更加动态。

    【讨论】:

    • 您对如何在浏览器重新加载时保留所选主题有什么建议吗?
    • @AaronLavers 对于一个简单的实现,我可能会使用 cookie。我用过的一个好包是“ngx-cookie-service”。您可以存储主题名称,然后在组件控制器中搜索 OnInit。
    • @AaronLavers 如果您希望主题在所有浏览器中对用户保持一致,除了(或代替)将用户选择的主题存储在 cookie/本地存储中,您可以将其存储在服务器端和加载网站时从服务器获取有关用户偏好的信息
    • 在 Angular 6 中运行良好。非常感谢!
    • 这个例子对我有用,除了 1 个细节:要在浅色或深色背景之间切换,我还必须更改 body 元素的样式,可以通过 document.getElementsByTagName('body')[0] 访问
    【解决方案2】:

    您必须使用OverlayContainergetContainerElement 方法。下面是一些用法示例:

    this.overlay.getContainerElement().classList.add('my-theme');
    

    至于你的样式文件,我强烈建议删除custom-theme.scsslight-custom-theme.scss 的这一行(在这种情况下你只需要你的类):

    @include angular-material-theme($custom-theme); // Remove this line from custom-theme.scss and light-custom-theme.scss
    

    如果您还想为您的应用切换主题,您可能应该在相同的toggleTheme 方法中使用它:

    toggleTheme(): void {
      if (this.overlay.classList.contains("custom-theme")) {
        this.overlay.classList.remove("custom-theme");
        this.overlay.classList.add("light-custom-theme");
      } else if (this.overlay.classList.contains("light-custom-theme")) {
        this.overlay.getContainerElement().classList.remove("light-custom-theme");
        this.overlay.classList.add("custom-theme");
      } else {
        this.overlay.classList.add("light-custom-theme");
      }
      if (document.body.classList.contains("custom-theme")) {
        document.body.classList.remove("custom-theme");
        document.body.classList.add("light-custom-theme");
      } else if (document.body.classList.contains("light-custom-theme")) {
        document.body.classList.remove("light-custom-theme");
        document.body.classList.add("custom-theme");
      } else {
        this.overlay.classList.add("light-custom-theme");
      }
    }
    

    【讨论】:

    • 太棒了!这几乎可以按预期工作,但是我的身体标签没有更新并且保持在浅色主题中。它显示为class="mat-app-background custom-theme",但仍然是浅色主题
    • 嗯,想通了,但我们真的需要使用侧导航吗?
    • @Surreal 您实际上不必将它放在 sidenav 中,只需确保 body 具有 mat-app-background 类,以便主题正常工作。
    • 如果你想使用正确的mat-app-background,你可以在函数toggleTheme()中将custom-them-css-class选择器分配给文档的根元素:document.documentElement.classList.add("custom-theme");加上class="mat-app-background"&lt;body&gt; 标签,正确的背景颜色将生效。当您在自定义 custom-them-css-class mixin 中调用 @include angular-material-theme($custom-theme); 时,mat-app-background css-selector 将成为嵌套选择器。
    【解决方案3】:

    您可以随时检查主题选择器是如何在https://material.angular.io/ 中实现的,并且只需执行相同的https://github.com/angular/material.angular.io/tree/master/src/app/shared/theme-picker 操作,您将拥有永久的解决方案,以防万一发生任何重大更改,您始终可以查找材料文档源如何修复。

    【讨论】:

      【解决方案4】:

      参考@Edric's solution

      使用local-storage来保留选定的主题

      这是我更新了工作代码的 Github 链接:~ Angular Material Theme Changer

      希望这也有帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-02-28
        • 2019-06-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-18
        • 2016-11-14
        相关资源
        最近更新 更多