Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 Material Dynamic Themes

Tags:

I've created my own scss theme and declared it in angular-cli.json, all works fine.

Now I need to dynamically change the theme.

I've tried to add the second theme in angular-cli.json, but as expected it overrides the first one.

So maybe one option would be to remove the theme declaration from angular-cli.json and have 2 components, each with it's own scss style, one overriding the other, the only difference between them being the styleUrls.

Or is there other recommended way to load dynamically a scss?

like image 892
Monica L Avatar asked Mar 14 '17 12:03

Monica L


2 Answers

As of Angular 5.1, this is how I achieved dynamic theme changes.

*Edit: This still works as of Angular 7+

Working editable example - https://stackblitz.com/edit/dynamic-material-theming

In my theme.scss file, I include a default theme(notice it isn't kept under a class name - this is so Angular will use it as the default), and then a light and dark theme.

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) } 

In the app.component file, I include OverlayContainer from @angular/cdk/overlay. You can find Angular's documentation for this here https://material.angular.io/guide/theming; though their implementation is a little different. Please note, I also had to include OverlayModule as an import in app.module as well.

In my app.component file, I also declared @HostBinding('class') componentCssClass; as a variable, which will be used to set the theme as a class.

app.component.ts

import {Component, HostBinding, OnInit} from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Version } from './classes/version'; import { OverlayContainer} from '@angular/cdk/overlay';  @Component({   selector: 'app-root',   templateUrl: './app.component.html',   styleUrls: ['./app.component.css'], }) export class AppComponent implements OnInit {    constructor(private http: HttpClient, public overlayContainer: OverlayContainer) {}    title = 'app';   version: Version;   @HostBinding('class') componentCssClass;    ngOnInit() {     this.getVersion();   }    onSetTheme(theme) {     this.overlayContainer.getContainerElement().classList.add(theme);     this.componentCssClass = theme;   }    getVersion() {     this.http.get<Version>('/api/version')       .subscribe(data => {         this.version = data;       });   }  } 

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 {} 

Finally, call the onSetTheme function from your view.

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> 

You might consider using an observable so that the functionality would be more dynamic.

like image 71
K. Waite Avatar answered Sep 28 '22 03:09

K. Waite


I found my answer in Change Material design theme for Angular 2. There is a good GIT example at https://github.com/jelbourn/material2-app.

So I use the same single scss theme file, but I added to it a new class for the new theme:

.m2app-dark {   $dark-primary: md-palette($md-pink, 700, 500, 900);   $dark-accent:  md-palette($md-blue-grey, A200, A100, A400);   $dark-warn:    md-palette($md-deep-orange);   $dark-theme: md-dark-theme($dark-primary, $dark-accent, $dark-warn);   @include angular-material-theme($dark-theme); } 

This one is used in the html, and is active or not depending on the value of a boolean:

 <md-sidenav-layout [class.m2app-dark]="isDarkTheme"> 
like image 43
Monica L Avatar answered Sep 28 '22 02:09

Monica L