Toggle a class by a button from header component that affect main app component by Angular 6+

爷,独闯天下 提交于 2020-03-03 17:17:17

问题


My app by Angular simply starts with index.html:

<body>
  <app-root></app-root>
</body>

I would like it to be switched between Dark/Light by toggling between <body> and <body class="dark-mode"> with below styles:

body abcxyz {
  color: #fff
}

body.dark-mode abcxyz {
  color: #a2b3c4
}

Next is app.component.html with Header - Main - Footer as usual:

<app-header></app-header>
<router-outlet></router-outlet>
<app-footer></app-footer>

The header.component.html has a toggle button:

<button (click)="onClickDarkMode()">Dark Mode</button>

We need a variable and a function like this to work, I put them in header.component.ts:

isDarkMode = false;
onClickDarkMode() {
  this.isDarkMode = !this.isDarkMode;
}

This idea seems simple to implement, but the click event seems like it fires to nothing with any of these lines added to the <body>:

<body [class.dark-mode]="isDarkMode">

or

<body [ngClass]="{'dark-mode': isDarkMode}">

or

<body [ngClass]="isDarkMode ? 'dark-mode' : ''">

Further more, with the idea of "Dark/Light", is this the best way to implement by toggling class inside the <body>?


回答1:


You're pretty close! I would highly recommend using a service so that you can effectively share state between components.

Basically what it looks like is a service:

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

@Injectable({
    providedIn: 'root'
})
export class ThemeService {

    /**
     * Variable to hold our setting.
     */
    public darkMode: boolean;

    /**
     * Enable/disable "dark mode" by flipping the bit.
     */
    public toggle(): void {

        this.darkMode = !this.darkMode;

    }

}

and the component:

<div class="wrapper" [class.dark-mode]="themeService.darkMode">

    <div class="text">

        Dark Mode Enabled? {{ themeService.darkMode ? 'YES' : 'NO' }}

    </div>

    <div class="button">

        Parent Component

        <button (click)="themeService.toggle()">Toggle!</button>

    </div>

    <app-child></app-child>

</div>

You will use dependency injection to "expose" the ThemeService like so:

import { Component }    from '@angular/core';
import { ThemeService } from './theme.service';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: [ './app.component.scss' ]
})
export class AppComponent {

    /**
     * Inject the theme service which will be called by our button (click).
     *
     * @param {ThemeService} themeService instance.
     */
    public constructor(public themeService: ThemeService) {

    }

}

By using a service you can not only share state but also wire up more properties like font-size, etc from a central location.

I have a brief tutorial explaining how to wire things up at https://matthewdavis.io/theme-service accompanied by the project repo https://github.com/mateothegreat/ng-byexamples-theme-service.

Hope this helps you along your journey. Feel free to ask questions!




回答2:


Components and their variables are scoped in Angular. This means, that a public variable in your component like isDarkMode is only known by the component itself and the template. There's no global representation of that variable.

While this works:

<app-header>
  <div [ngClass]="{'dark-mode': isDarkMode}">
</app-header>

This won't:

<body [ngClass]="{'dark-mode': isDarkMode}"></body>

What you can do is to set the class of the body-element in your component. But keep in mind that you should avoid manipulating the DOM directly and that your application should be platform agnostic.

That's why you should use Angular's dependency injection:

import { Component, OnInit, Renderer2, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.less']
})

export class HeaderComponent implements OnInit {
  darkMode: boolean = false;

  constructor(
    @Inject(DOCUMENT) private document: Document,
    private renderer: Renderer2,
  ) {}

  ngOnInit() {}

  toggleDarkMode() {
    this.darkMode = !this.darkMode;

    if (this.darkMode) {
      this.renderer.addClass(this.document.body, 'dark-mode');
    } else {
      this.renderer.removeClass(this.document.body, 'dark-mode');
    } 
  }
}

Now only the HeaderComponent can change the class. You should make this more versatile by using a service, so that you can do things like:

  • change the mode from any place in your application
  • through events make it possible for other components to be informed when the mode is changed


来源:https://stackoverflow.com/questions/59123292/toggle-a-class-by-a-button-from-header-component-that-affect-main-app-component

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!