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

Angular实现自定义主题后*ngIf值变更视图不更新问题排查

Angular视图不更新问题排查与解决

问题背景

我是Angular和前端开发新手,在大型项目中实现自定义主题后,出现修改ngIf值时HTML视图不再更新的问题。最初怀疑是主题实现方式导致,遂分享主题相关代码:

styles.scss

/* You can add global styles to this file, and also import other style files */
@import 'custom-theme.scss';

html,
body {
    height: 100%;
}

body {
    margin: 0;
    font-family: Roboto, "Helvetica Neue", sans-serif;
}

custom-theme.scss

@use '@angular/material' as mat;
@include mat.core();

$light-primary: mat.define-palette(mat.$blue-palette);
$light-accent: mat.define-palette(mat.$indigo-palette);
$light-warn: mat.define-palette(mat.$red-palette);

$light-theme: mat.define-light-theme((color:(primary: $light-primary,
                accent: $light-accent,
                warn: $light-warn )));

@include mat.all-component-themes($light-theme);

.dark-theme {
    $dark-primary: mat.define-palette(mat.$deep-orange-palette);
    $dark-accent: mat.define-palette(mat.$amber-palette);
    $dark-warn: mat.define-palette(mat.$red-palette);

    $dark-theme: mat.define-dark-theme((color:(primary: $dark-primary,
                    accent: $dark-accent,
                    warn: $dark-warn )));

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

.light-background {
    background-color: rgba(0, 0, 0, 0.02);
}

.dark-background {
    background-color: #303030;
}

主题切换逻辑(header.component.ts)

constructor(
    public cookieService: CookieService,
    public dialog: MatDialog,
    public router: Router,
    public translateService: TranslateService,
    private responsive: BreakpointObserver,
    private render: Renderer2,
    @Inject(DOCUMENT) private document: Document,
    media: MediaMatcher,
  ) {
    this.mobileQuery = media.matchMedia('(max-width: 600px)');
    this.langs = this.translateService.getLangs();
    this.selectedLang = this.translateService.currentLang;
    this.userId = cookieService.get('UserId');
  }

  ngOnInit() {
    this.responsive.observe(['(min-width: 1022px)'])
      .subscribe(result => {
        this.isMobile = result.matches;
      });
  }

  changedTheme(): void {
    this.render.removeClass(this.document.body, 'light-theme');
    this.render.removeClass(this.document.body, 'dark-theme');
    this.render.removeClass(this.document.body, 'light-background');
    this.render.removeClass(this.document.body, 'dark-background');
    this.render.addClass(this.document.body, this.selectedTheme.toLowerCase() + '-theme');
    this.render.addClass(this.document.body, this.selectedTheme.toLowerCase() + '-background');
  }

注:changedTheme函数被复制到app.component.ts和settings.component.ts中,用于多组件主题管理;添加背景类是因主题未自动更改背景色。

更新

回退到主题实现前版本后问题仍存在,排查环境因素(重启、重下node_modules、多浏览器)无效,移除@angular/flex-layout也非诱因,不知调试方向。

更新2

经逐commit调试,发现问题根源是header.html组件的响应式代码故障,恢复到未做响应式修改的旧版代码后问题解决。故障代码与恢复后代码如下:

故障代码

<div style="display: contents;">
  <mat-toolbar color="primary" class="header-top">
    <div style="display: contents;" *ngIf="!isMobile">
      <button mat-icon-button (click)="snav.toggle()" class="icon-button">
        <img class="icon" src="../assets/imgs/1315254.png" alt="profile" />
      </button>

      <span class="toolbar-text" (click)="goToPage('home')">{{ cookieService.get('Username') }}</span>

      <button mat-icon-button (click)="goToPage('access')" *ngIf="!userId">
        <mat-icon>manage_accounts</mat-icon>
      </button>
    </div>

    <div style="display: contents;" *ngIf="isMobile">
      <span class="toolbar-text" (click)="goToPage('home')">FindME!</span>
      <span class="spacer"></span>

      <mat-form-field style="margin-top: 20px;" *ngIf="isDevMode()">
        <mat-select [(value)]="selectedLang" (valueChange)="changedLanguage()" id="lang-select">
          <mat-option *ngFor="let lang of langs" [value]="lang">
            {{ lang.split('-')[0].toUpperCase() }}
          </mat-option>
        </mat-select>
      </mat-form-field>

      <button mat-icon-button (click)="goToPage('access')" *ngIf="!userId">
        <mat-icon>manage_accounts</mat-icon>
      </button>
      <button mat-icon-button [matMenuTriggerFor]="notifications" *ngIf="userId">
        <mat-icon>notifications</mat-icon>
      </button>
      <button mat-icon-button [matMenuTriggerFor]="chat" *ngIf="userId">
        <mat-icon>chat</mat-icon>
      </button>
      <button mat-icon-button class="icon-button" [matMenuTriggerFor]="menu" *ngIf="userId">
        <img class="icon" src="../assets/imgs/1315254.png" alt="profile" />
      </button>

      <!-- Menu -->
      <mat-menu #menu="matMenu">
        <button mat-menu-item (click)="goToPage('profile')">
          <mat-icon>account_circle</mat-icon>
          <span>{{ 'HEADER.PROFILE' | translate }}</span>
        </button>
        <button mat-menu-item (click)="goToPage('settings')">
          <mat-icon>settings</mat-icon>
          <span>{{ 'HEADER.SETTINGS' | translate }}</span>
        </button>
        <button mat-menu-item (click)="logout()">
          <mat-icon>logout</mat-icon>
          <span>{{ 'HEADER.LOGOUT' | translate }}</span>
        </button>
      </mat-menu>

      <!-- Notifications -->
      <mat-menu #notifications="matMenu">
        <h1 *ngIf="userNotifications == null">{{ "HEADER.NO-NEW-NOTIFICATIONS" | translate }}</h1>
        <div *ngIf="userNotifications != null">
          <button mat-menu-item (click)="goToPage('profile')">
            <mat-icon>notifications</mat-icon>
          </button>
        </div>
      </mat-menu>

      <!-- Chat -->
      <mat-menu #chat="matMenu">
        <h1 *ngIf="chats == null">{{ "HEADER.NO-NEW-CONVERSATION" | translate }}</h1>
        <div *ngIf="chats != null">
          <button mat-menu-item (click)="goToPage('profile')">
            <mat-icon>chat</mat-icon>
          </button>
        </div>
      </mat-menu>

      <button mat-icon-button aria-label="icon-button with share icon" (click)="goToPage('about')">
        <mat-icon>help</mat-icon>
      </button>
    </div>
  </mat-toolbar>

  <mat-sidenav-container class="example-sidenav-container">
    <mat-sidenav #snav [mode]="'over'" fixedTopGap="102">
      <mat-nav-list>
        <mat-selection-list role="list">
          <mat-divider></mat-divider>
          <mat-list-item role="button" *ngIf="userId">
            <a (click)="goToPage('notifications')">
              {{ 'NOTIFICATIONS' | translate }}
            </a>
          </mat-list-item>
          <mat-divider></mat-divider>
          <mat-list-item role="button" *ngIf="userId">
            <a (click)="goToPage('chat')">
              {{ 'CHATS' | translate }}
            </a>
          </mat-list-item>
          <mat-divider></mat-divider>
        </mat-selection-list>
      </mat-nav-list>
    </mat-sidenav>
    <mat-sidenav-content>
      <router-outlet></router-outlet>
    </mat-sidenav-content>
  </mat-sidenav-container>
</div>

恢复后的正常代码

<mat-toolbar color="primary" class="header-top">
  <span class="toolbar-text" (click)="goToPage('home')">FindME!</span>
  <span class="spacer"></span>
  <mat-form-field>
    <mat-select [(value)]="selectedLang" (valueChange)="changedLanguage()" id="lang-select">
      <mat-option *ngFor="let lang of langs" [value]="lang">
        <!-- {{ lang | translate }} -->
        {{ lang.split('-')[0].toUpperCase() }}
      </mat-option>
    </mat-select>
  </mat-form-field>

  <button mat-icon-button (click)="goToPage('access')" *ngIf="!cookieService.get('UserId')">
    <mat-icon>manage_accounts</mat-icon>
  </button>
  <button mat-icon-button [matMenuTriggerFor]="notifications" *ngIf="cookieService.get('UserId')">
    <mat-icon>notifications</mat-icon>
  </button>
  <button mat-icon-button [matMenuTriggerFor]="chat" *ngIf="cookieService.get('UserId')">
    <mat-icon>chat</mat-icon>
  </button>
  <button mat-icon-button class="icon-button" [matMenuTriggerFor]="menu" *ngIf="cookieService.get('UserId')">
    <img class="icon" src="../assets/imgs/1315254.png" alt="profile" />
  </button>

  <!-- Menu -->
  <mat-menu #menu="matMenu">
    <button mat-menu-item (click)="goToPage('profile')">
      <mat-icon>account_circle</mat-icon>
      <span>{{ 'HEADER.PROFILE' | translate }}</span>
    </button>
    <button mat-menu-item (click)="goToPage('settings')">
      <mat-icon>settings</mat-icon>
      <span>{{ 'HEADER.SETTINGS' | translate }}</span>
    </button>
    <button mat-menu-item (click)="logout()">
      <mat-icon>logout</mat-icon>
      <span>{{ 'HEADER.LOGOUT' | translate }}</span>
    </button>
  </mat-menu>
  <!-- Notifications -->
  <mat-menu #notifications="matMenu">
    <h1 *ngIf="userNotifications == null">{{ "HEADER.NO-NEW-NOTIFICATIONS" | translate }}</h1>
    <div *ngIf="userNotifications != null">
      <button mat-menu-item (click)="goToPage('profile')">
        <mat-icon>notifications</mat-icon>
      </button>
    </div>
  </mat-menu>
  <!-- Chat -->
  <mat-menu #chat="matMenu">
    <h1 *ngIf="chats == null">{{ "HEADER.NO-NEW-CONVERSATION" | translate }}</h1>
    <div *ngIf="chats != null">
      <button mat-menu-item (click)="goToPage('profile')">
        <mat-icon>chat</mat-icon>
      </button>
    </div>
  </mat-menu>

  <button mat-icon-button aria-label="icon-button with share icon" (click)="goToPage('about')">
    <mat-icon>help</mat-icon>
  </button>
</mat-toolbar>

结论

最终确认问题源于自行编写的响应式代码,已通过恢复旧版代码解决,抱歉浪费大家时间。


内容的提问来源于stack exchange,提问作者Alfredo Marin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 07:47:05