Angular Material 18 SideNav autosize失效:折叠时宽度无法自适应
问题核心
在mode="side"模式下,mat-sidenav的autosize属性不会自动监听内部元素的显示/隐藏变化来调整宽度,再加上mat-nav-list默认布局会预留空间,导致折叠侧边栏后宽度仍保持固定。
具体解决方案
1. 绑定动态状态并手动触发侧边栏重计算
先获取mat-sidenav实例,切换展开状态时强制触发宽度重计算,同时通过类控制布局:
修改HTML代码:
<mat-sidenav-container autosize="true"> <mat-sidenav #sidenav mode="side" opened [ngClass]="{'sidenav-collapsed': !isExpanded}"> <mat-nav-list> <mat-list-item (click)="toggleSidenav()"> <mat-icon mat-list-icon class="chevron-button" [ngClass]="{ rotated: isExpanded }">chevron_right</mat-icon> </mat-list-item> @for (item of menuItems; track $index) { <mat-list-item [routerLink]="item.path"> <mat-icon matListItemIcon>{{ item.data?.['icon'] || 'default_icon' }}</mat-icon> <span *ngIf="isExpanded" class="menu-item">{{ item.title }}</span> </mat-list-item> } </mat-nav-list> </mat-sidenav> <mat-sidenav-content> <router-outlet></router-outlet> </mat-sidenav-content> </mat-sidenav-container>
修改TS代码:
添加视图引用获取侧边栏实例,实现切换方法并触发宽度重计算:
import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core'; import { routes } from '../../app.routes'; import { RouterModule } from '@angular/router'; import { MatSidenav, MatSidenavModule } from '@angular/material/sidenav'; import { MatButtonModule } from '@angular/material/button'; import { MatRadioModule } from '@angular/material/radio'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatIconModule } from '@angular/material/icon'; import { MatListModule } from '@angular/material/list'; import { MatToolbarModule } from '@angular/material/toolbar'; @Component({ selector: 'app-side-menu', standalone: true, imports: [ CommonModule, RouterModule, MatButtonModule, MatRadioModule, FormsModule, ReactiveFormsModule, MatSidenavModule, MatIconModule, MatListModule, MatToolbarModule ], templateUrl: './side-menu.component.html', styleUrl: './side-menu.component.css', changeDetection: ChangeDetectionStrategy.OnPush, }) export class SideMenuComponent { @ViewChild('sidenav') sidenav!: MatSidenav; public menuItems = routes .map( route => route.children ?? [] ) .flat() .filter( route => route?.path ) .filter( route => !route.path?.includes(':') ) isExpanded: boolean = true; toggleSidenav() { this.isExpanded = !this.isExpanded; // 延迟触发,确保DOM更新后再计算宽度 setTimeout(() => { this.sidenav.resize(); }, 0); } }
2. 添加CSS控制折叠状态布局
定义折叠时的侧边栏和列表样式,避免多余留白:
:host { display: block; } mat-sidenav-container { height: 100%; } .menu-item { margin-left: 8px; } .chevron-button { transition: 300ms ease-in-out; transform: rotate(0deg); } .chevron-button.rotated { transform: rotate(180deg); } /* 折叠状态样式 */ .sidenav-collapsed { width: fit-content !important; min-width: 60px; /* 保证图标完整显示 */ } .sidenav-collapsed mat-nav-list { padding: 0; } .sidenav-collapsed mat-list-item { justify-content: center; }
3. 适配OnPush变更检测策略
因为组件用了ChangeDetectionStrategy.OnPush,切换isExpanded后通过setTimeout确保DOM更新完成再调用resize,保证视图同步更新。
生效原理
sidenav.resize()强制侧边栏重新计算宽度,配合autosize属性实现自适应- 折叠状态的CSS消除默认布局的多余空间,让宽度贴合内容
- 手动触发resize解决了
mode="side"下自动监听内部元素变化失效的问题
内容的提问来源于stack exchange,提问作者Imad
相关产品推荐
相关产品推荐

