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
相关产品推荐
相关产品推荐

