You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular暗黑模式切换语言时card-content背景消失问题求助

问题:暗黑模式下切换语言时card-content背景消失

在Angular项目中通过<mat-slide-toggle>实现明暗模式切换,但暗黑模式下使用ngx-translate切换语言时,card-content的background属性会消失。


相关代码

home-component.html

<section>
<div class="container">
    <h1 class="products-title">New Arrivals</h1>
    <div class="products-list">
      <div *ngFor="let product of products" class="card">
        <div class="card-content" [ngStyle]="{'background-color': isDarkTheme ? '#5eda5e' : '#3b5b2d'}">
          <img class="product-image" [src]="product.imgSrc" [alt]="">
          <h2 class="product-title">{{ product.name | translate}}</h2>
          <p class="product-description">{{ product.description | translate}}</p>
          <p class="product-price">{{ product.price | currency:'USD':'symbol':'1.2-2' }}</p>
        </div>    
      </div>
    </div>
  </div>
</section>

home-component.ts

import { AfterViewInit, Component, OnInit } from '@angular/core';
import { ProductService } from '../product.service';
import { Product } from '../product.model';
import { TranslateService } from '@ngx-translate/core';
import { Subscription } from 'rxjs';

@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit, AfterViewInit {
products: Product[] = [];
isDarkTheme: boolean = false;
langChangeSubscription: Subscription;

constructor(private productService: ProductService, public translate: TranslateService) {
this.translate.setDefaultLang('en');
this.translate.use('en');
translate.addLangs(['en', 'tr']);

    this.langChangeSubscription = this.translate.onLangChange.subscribe(() => {
      this.updateProducts();
    });

}

ngAfterViewInit(): void {
console.log('Dark mode:', this.isDarkTheme);
}

ngOnInit(): void {
this.isDarkTheme = localStorage.getItem('theme') === "Dark";
this.updateProducts();
}

ngOnDestroy(): void {
this.langChangeSubscription.unsubscribe();
}

switchLanguage(lang: string) {
this.translate.use(lang);
}

ChangeLang(event: any) {
const lang = event.target.value;
this.switchLanguage(lang);
}

private updateProducts() {
this.products = this.productService.getProducts();
}
}

header-component.html

<nav class="navigation">
    <ul class="nav-bar" [ngClass]="{'dark-theme-mode':isDarkTheme}">
        <li>
            <a [routerLink]="'/home'">
                <img class="logo" src="assets/img/goodfellas-logo.png">
            </a>
        </li>
        <input type="checkbox" id="check">
        <span class="menu" [ngClass]="{'dark-theme-mode':isDarkTheme}">
            <li>
                <a class="fruit-link" [routerLink]="'/fruits'"> <img class="fruit-icon" src="assets/img/fruit.png">{{ 'fruit_title' | translate }}</a>
            </li>
            <li>
                <a class="vegetable-link" [routerLink]="'/vegetables'"><img class="vegetable-icon" src="assets/img/vegetable.png">{{ 'vegetable_title' | translate }}</a>
            </li>
            <li>
                <a [routerLink]="'/drinks'"><img class="drink-icon" src="assets/img/water.png">{{ 'drink_title' | translate }}</a>
            </li>
            <li>
                <a [routerLink]="'/login'"><img class="login-icon" src="assets/img/login.png">{{ 'login' | translate }}</a>
            </li>
            <li>
                <a [routerLink]="'/not-registered-sign-in'"><img class="register-icon" src="assets/img/register.png">{{ 'register' | translate }}</a>
            </li>
            <li>
                <img class="shopping-cart" src="assets/img/shopping-cart.png">
            </li>
            <li>
                <form class="languages">
                    <label for="language">Language/Dil:</label>
                    <select class="form-select" (change)="ChangeLang($event)" id="languageSelect" [ngClass]="{'dark-theme-mode':isDarkTheme}">
                       <option [value]="'en'" [selected]="translate.currentLang === 'en'">English</option>
                       <option [value]="'tr'" [selected]="translate.currentLang === 'tr'">Türkçe</option>
                    </select>
                 </form>                                         
            </li>
            <li>
                <!-- part for dark mode/light mode switch button -->
                <mat-slide-toggle [(ngModel)]="isDarkTheme" labelPosition="before" (change)="storeThemeSelection()">
                    <img [src]="isDarkTheme ? dark_mode : light_mode" alt="Theme Image">
                </mat-slide-toggle>                                         
            </li>
            <label for="check" class="close-menu"><i class="fas fa-times"></i></label>
        </span>
        <label for="check" class="open-menu">
            <i class="fas fa-bars"></i>
        </label>
    </ul>
</nav>

header-component.ts

import { Component, Renderer2 } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';

@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrl: './header.component.css'
})
export class HeaderComponent {
isDarkTheme: boolean = false;

// Initialize dark_mode and light_mode with translation keys
dark_mode: string = 'assets/img/crescent-moon.png';
light_mode: string = 'assets/img/sunny.png';

constructor(public translate: TranslateService, private renderer: Renderer2) {
// Set default language
this.translate.setDefaultLang('en');
// Set initial language if needed
this.translate.use('en');
translate.addLangs(['en', 'tr']);
}

ngOnInit(): void {
this.isDarkTheme = localStorage.getItem('theme') === "Dark";
this.translate.get('assets/img/crescent-moon.png').subscribe((res: string) => {
this.dark_mode = res;
});
this.translate.get('assets/img/sunny.png').subscribe((res: string) => {
this.light_mode = res;
});
this.setBodyBackground();
this.productText();
}

switchLanguage(lang: string) {
this.translate.use(lang);
}

ChangeLang(event: any) {
const lang = event.target.value;
this.switchLanguage(lang);
localStorage.setItem('theme', this.isDarkTheme ? 'Dark' : 'Light');
this.languageBackground();
}

storeThemeSelection() {
localStorage.setItem('theme', this.isDarkTheme ? 'Dark' : 'Light');
this.setBodyBackground();
this.productText();
this.footerBackground();
this.cardBackground();
}

private setBodyBackground() {
const backgroundColor = this.isDarkTheme ? '#3b5b2d' : '#5eda5e';
this.renderer.setStyle(document.body, 'background-color', backgroundColor);
}

private productText() {
const color = this.isDarkTheme ? '#ffffff' : '#000000';
this.renderer.setStyle(document.body, 'color', color);
}

private footerBackground() {
const footerBackgroundColor = this.isDarkTheme ? '#4d4d4d' : '#ffffff';
this.renderer.setStyle(document.querySelector('.footer'), 'background-color', footerBackgroundColor);
}

private languageBackground() {
const color = this.isDarkTheme ? '#ffffff' : '#000000';
this.renderer.setStyle(document.body, 'color', color);
}

private cardBackground() {
const backgroundColor = this.isDarkTheme ? '#5eda5e' : '#3b5b2d';
const cardContents = document.querySelectorAll('.card-content');
cardContents.forEach(cardContent => {
this.renderer.setStyle(cardContent, 'background-color', backgroundColor);
});
}  
}

问题原因

  1. 组件状态不同步:Header和Home组件的isDarkTheme是独立变量,切换语言时Header的状态没有同步到Home,导致Home的[ngStyle]绑定逻辑失效。
  2. DOM操作冲突:Header通过document.querySelectorAll手动修改card样式,但切换语言时updateProducts()会重新渲染列表,新生成的card-content不会被覆盖,且手动DOM操作会被Angular变更检测覆盖。

解决方案

1. 创建全局主题服务统一管理状态

// theme.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class ThemeService {
  private isDarkThemeSubject = new BehaviorSubject<boolean>(false);
  isDarkTheme$: Observable<boolean> = this.isDarkThemeSubject.asObservable();

  constructor() {
    const savedTheme = localStorage.getItem('theme') === 'Dark';
    this.isDarkThemeSubject.next(savedTheme);
    this.updateGlobalStyles(savedTheme);
  }

  toggleTheme() {
    const newState = !this.isDarkThemeSubject.value;
    this.isDarkThemeSubject.next(newState);
    localStorage.setItem('theme', newState ? 'Dark' : 'Light');
    this.updateGlobalStyles(newState);
  }

  private updateGlobalStyles(isDark: boolean) {
    document.body.style.backgroundColor = isDark ? '#3b5b2d' : '#5eda5e';
    document.body.style.color = isDark ? '#ffffff' : '#000000';
    const footer = document.querySelector('.footer');
    if (footer) footer.style.backgroundColor = isDark ? '#4d4d4d' : '#ffffff';
  }
}

2. 修改Header组件使用主题服务

// header-component.ts
import { Component } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { ThemeService } from './theme.service';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent {
  isDarkTheme: boolean = false;
  dark_mode: string = 'assets/img/crescent-moon.png';
  light_mode: string = 'assets/img/sunny.png';

  constructor(
    public translate: TranslateService,
    private themeService: ThemeService
  ) {
    this.translate.setDefaultLang('en');
    this.translate.use('en');
    this.translate.addLangs(['en', 'tr']);
    
    this.themeService.isDarkTheme$.subscribe(state => {
      this.isDarkTheme = state;
    });
  }

  ngOnInit(): void {
    this.translate.get('assets/img/crescent-moon.png').subscribe(res => {
      this.dark_mode = res;
    });
    this.translate.get('assets/img/sunny.png').subscribe(res => {
      this.light_mode = res;
    });
  }

  switchLanguage(lang: string) {
    this.translate.use(lang);
  }

  ChangeLang(event: any) {
    const lang = event.target.value;
    this.switchLanguage(lang);
  }

  storeThemeSelection() {
    this.themeService.toggleTheme();
  }
}

3. 修改Home组件订阅主题状态

// home-component.ts
import { AfterViewInit, Component, OnInit, OnDestroy } from '@angular/core';
import { ProductService } from '../product.service';
import { Product } from '../product.model';
import { TranslateService } from '@ngx-translate/core';
import { Subscription } from 'rxjs';
import { ThemeService } from '../theme.service';

@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
products: Product[] = [];
isDarkTheme: boolean = false;
langChangeSubscription: Subscription;
private themeSubscription: Subscription;

constructor(
  private productService: ProductService,
  public translate: TranslateService,
  private themeService: ThemeService
) {
this.translate.setDefaultLang('en');
this.translate.use('en');
this.translate.addLangs(['en', 'tr']);

    this.langChangeSubscription = this.translate.onLangChange.subscribe(() => {
      this.updateProducts();
    });
}

ngAfterViewInit(): void {
console.log('Dark mode:', this.isDarkTheme);
}

ngOnInit(): void {
this.themeSubscription = this.themeService.isDarkTheme$.subscribe(state => {
  this.isDarkTheme = state;
});
this.updateProducts();
}

ngOnDestroy(): void {
this.langChangeSubscription.unsubscribe();
this.themeSubscription.unsubscribe();
}

switchLanguage(lang: string) {
this.translate.use(lang);
}

ChangeLang(event: any) {
const lang = event.target.value;
this.switchLanguage(lang);
}

private updateProducts() {
this.products = this.productService.getProducts();
}
}

4. 移除Header中的DOM操作代码

删除Header组件内setBodyBackground、productText、footerBackground、languageBackground、cardBackground这些手动操作DOM的方法,全部由主题服务统一处理。


内容的提问来源于stack exchange,提问作者MR.NOTHING

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 04:09:51