I been reading a few articles on this but they seem to be conflicting in several different ways. I am hoping to re-create the same theme switching as the angular material do
You have to use the getContainerElement
method of OverlayContainer
. Here's some example usage:
this.overlay.getContainerElement().classList.add('my-theme');
As for your style files, I strongly suggest removing this line for both custom-theme.scss
and light-custom-theme.scss
(you only need it for your classes in this case):
@include angular-material-theme($custom-theme); // Remove this line from custom-theme.scss and light-custom-theme.scss
If you also want to toggle the theme for your app, you should probably use this in the same toggleTheme
method:
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");
}
}
With reference to @Edric's solution
Used local-storage to retain the selected theme.
Here is my Github Link where I have updated the working code:~ Angular Material Theme Changer
Hope this is also helpful.
You cam always check how the theme picker is being implemented in https://material.angular.io/ and just do the same https://github.com/angular/material.angular.io/tree/master/src/app/shared/theme-picker by doing so you will have the everlasting solution as in case any breaking changes you always can lookup material docs source how to fix.
Here's an alternative solution for Angular 5.1+/Angular Material 5.0+.
*Edit: As noted, this still works in Angular 7+.
Working editable example - https://stackblitz.com/edit/dynamic-material-theming
In theme.scss, 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, 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 } 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 {}
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.