Search code examples
htmlcssangularangular10

How can I change a body tag class in Angular 10 (best practice)?


I want to switch between two classes (light and dark) at TAG Body.

What I did? I created a service:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class ThemeService {
  body = document.body;

  constructor() { }

  changeLight() {
    this.body.classList.replace('light', 'dark');
  }

  changeDark() {
    this.body.classList.replace('dark', 'light');
  }
}

It is working as expected but I know that this code does not use best practices.
What is the correct way to change between these two classes?


Solution

  • Edit: Added a service to the stackblitz, but again, there are many ways to do this. This is just a starting point.

    While the "right way" is subjective, you have some options to make it "Angular-y"

    Component:

    import { Component, Inject } from '@angular/core';
    import { DOCUMENT } from '@angular/common';
    
    // Create a type that accepts either the string 'light' or 'dark' only
    type Theme = 'light' | 'dark';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: [ './app.component.css' ]
    })
    export class AppComponent  {
      // Default to 'light' theme
      currentTheme: Theme = 'light';
    
      // Inject document which is safe when used with server-side rendering
      constructor(@Inject(DOCUMENT) private document: Document) {
        // Add the current (light) theme as a default
        this.document.body.classList.add(this.currentTheme);
      }
    
      // Swap them out, and keep track of the new theme
      switchTheme(newTheme: Theme): void {
        this.document.body.classList.replace(this.currentTheme, newTheme)
        this.currentTheme = newTheme;
      }
    }
    

    HTML:

    <p>
      Current theme: {{ currentTheme }}
    
      <button (click)="switchTheme('light')">Light mode</button>
      <button (click)="switchTheme('dark')">Dark mode</button>
    </p>
    

    Many ways to do this, but one benefit of defining the types is if you provide a bad value, such as:

    <p>
      Current theme: {{ currentTheme }}
    
      <button (click)="switchTheme('light')">Light mode</button>
      <button (click)="switchTheme('dark')">Dark mode</button>
      <button (click)="switchTheme('noop')">Invalid</button>
    </p>
    

    You'll get an error:

    Argument of type '"noop"' is not assignable to parameter of type 'Theme'.

    StackBlitz