Angular Material – Dark Mode Changing Background Image

Issue

Currently I have an app component that has a sidenav and my router-outlet in the sidenav content. In this component I manage the app theme by adding or removing the darkMode class in my style.scss.

Is there a way to control what background-image I’m using?

What if I also wanted to control the background image based on what component is opened on the router-outlet? Would that be possible to? Maybe not setting the background-image in the app component but on the opened component instead

app.component.html

<mat-sidenav-container autosize class="h-100">
    <mat-sidenav #sidenav mode="side" opened="true">
        <mat-nav-list>
            <mat-list-item>
                <mat-slide-toggle [formControl]="toggleControl"></mat-slide-toggle>
            </mat-list-item>
            ...
        <mat-sidenav-content>
            <router-outlet></router-outlet>
        </mat-sidenav-content>

app.component.ts

@HostBinding('class') className = '';
toggleControl = new FormControl(false);

constructor(private overlay: OverlayContainer, ...) { }

ngOnInit() {
    this.toggleControl.valueChanges.subscribe((darkMode) => {
      const darkClassName = 'darkMode';
      this.className = darkMode ? darkClassName : '';
      if (darkMode) {
        this.overlay.getContainerElement().classList.add(darkClassName);
      } else {
        this.overlay.getContainerElement().classList.remove(darkClassName);
      }
    });

    this.subscription = this._service.currentLogStatus.subscribe(logStatus => this.loggedIn = logStatus);
}

app.component.scss

mat-sidenav-container {
    background-image: url('../assets/img/home-background.png');
}

style.scss

.darkMode {
  @include mat.all-component-colors($angular-dark-theme);
}

Update

After some indications I’ve tried.

The darkMode background still presists on the ProfileComponent, even though I have another background referenced.

app.component.html

<mat-sidenav-content>
        <router-outlet (activate)="onRouterOutletActivate($event)"></router-outlet>
</mat-sidenav-content>

app.component.ts

ngOnInit() {
    this.toggleControl.valueChanges.subscribe((toggled) => {
      console.log(this.currentComponent);
      this.className = toggled ? 'darkMode' : '';
      
      if (toggled) {
        if (this.currentComponent == "home") {
          this._overlay.getContainerElement().classList.add('darkMode');
          this._overlay.getContainerElement().classList.remove('darkModeProfile');
        }

        if (this.currentComponent == "profile") {
          this._overlay.getContainerElement().classList.add('darkModeProfile');
          this._overlay.getContainerElement().classList.remove('darkMode');
        }
      } else {
        if (this.currentComponent == "home") {
          this._overlay.getContainerElement().classList.remove('darkMode');
        }

        if (this.currentComponent == "profile") {
          this._overlay.getContainerElement().classList.remove('darkModeProfile');
        }
      }
    });

    this.subscription = this._service.currentLogStatus.subscribe(logStatus => this.loggedIn = logStatus);
  }

  public onRouterOutletActivate(event : any) {
    if (event.constructor.name == "ProfileComponent") {
      this.currentComponent = "profile";
    }

    if (event.constructor.name == "HomeComponent") {
      this.currentComponent = "home";
    }
  }

app.component.scss

mat-sidenav-container {
    background-image: url('../assets/img/home-background.png');
}

:host-context(.darkMode) {
    mat-sidenav-container {
        background-image: url('../assets/img/home-background-dark.png');
    }
}

:host-context(.darkModeProfile) {
    mat-sidenav-container {
        background-image: url('../assets/img/profile-background.png');
    }
}

Solution

The solution that worked for me was to maintain the .darkMode class and use different backgrounds based on what component is being rendered by router-outlet

Solution

app.component.html

<mat-sidenav-container [ngStyle]="{ 'background': 
    'linear-gradient( rgba(0, 0, 0,' + theme + '), rgba(0, 0, 0,' + theme + ')),'+ 
    'url(../assets/img/' + currentComponent + '-background' + '.png)'}"
    autosize class="h-100">
    <mat-sidenav #sidenav mode="side" opened="true">
        <mat-nav-list>
            <mat-list-item>
                <mat-slide-toggle [formControl]="toggleControl"></mat-slide-toggle>
            </mat-list-item>
            ...
        <mat-sidenav-content>
            <router-outlet (activate)="onRouterOutletActivate($event)"></router-outlet>
        </mat-sidenav-content>
    </mat-sidenav>
</mat-sidenav-container>

app.component.ts

currentComponent: string = "home";
theme: number = 0;

constructor(private _overlay: OverlayContainer) { }

ngOnInit() {
  this.toggleControl.valueChanges.subscribe((toggled) => {
    this.className = toggled ? 'darkMode' : '';
      
    if (toggled) {
      this._overlay.getContainerElement().classList.add('darkMode');
      this.theme = 0.5;
    } else {
      this._overlay.getContainerElement().classList.remove('darkMode');
      this.theme = 0;
    }
  });

  this.subscription = this._service.currentLogStatus.subscribe(logStatus => this.loggedIn = logStatus);
}

public onRouterOutletActivate(event : any) {
  if (event.constructor.name == "ProfileComponent" || event.constructor.name == "RegisterComponent") {
    this.currentComponent = "profile";
  }

  if (event.constructor.name == "HomeComponent") {
    this.currentComponent = "home";
  }
}

style.scss

.darkMode {
  @include mat.all-component-colors($angular-dark-theme);
}

Doing it this way, I can control what image is shown and apply a filter when the .darkMode is enabled.

Answered By – André Clérigo

Answer Checked By – Katrina (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.