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); }); } }
问题原因
- 组件状态不同步:Header和Home组件的
isDarkTheme是独立变量,切换语言时Header的状态没有同步到Home,导致Home的[ngStyle]绑定逻辑失效。 - 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
相关产品推荐
相关产品推荐

