Angular中Service调用BreakpointObserver在app-component不生效问题
问题根因
@ViewChild 是 Angular 专为组件、指令提供的视图查询装饰器,只能在持有视图实例的组件/指令类中使用,才能查询到当前模板内的DOM或组件实例。Service是全局共享的逻辑层类,本身不关联任何组件视图,所以写在Service里的@ViewChild(MatSidenav)永远拿不到实际的侧边栏实例,调用方法时访问this.sidenav会直接报错,功能自然失效。
修复方案
调整逻辑,把sidenav实例的查询放在组件侧,再传递给Service的方法使用即可:
1. 修改app.component.ts
在组件内查询sidenav实例,调用Service方法时传入:
import { Component, ViewChild, AfterViewInit } from '@angular/core'; import { MyDataServiceService } from './my-data-service.service'; import { MatSidenav } from '@angular/material/sidenav'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent implements AfterViewInit { title = 'azad-app'; name = ''; // 组件侧查询侧边栏实例 @ViewChild(MatSidenav) sidenav!: MatSidenav; constructor(private user: MyDataServiceService) { } ngAfterViewInit() { // 调用Service方法时传入sidenav实例 this.user.menuDisplay(this.sidenav); } }
2. 修改my-data-service.service.ts
删除Service内的@ViewChild代码,调整menuDisplay方法接收sidenav参数:
import { Injectable } from '@angular/core'; import { MatSidenav } from '@angular/material/sidenav'; import { BreakpointObserver } from '@angular/cdk/layout'; import { Subscription } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class MyDataServiceService { private breakpointSub?: Subscription; constructor(private observer: BreakpointObserver) { } menuDisplay(sidenav: MatSidenav) { // 先销毁之前的订阅避免内存泄漏 this.breakpointSub?.unsubscribe(); this.breakpointSub = this.observer.observe(['(max-width:800px)']).subscribe((res) => { if (res.matches) { sidenav.mode = 'over'; sidenav.close(); } else { sidenav.mode = "side"; sidenav.open(); } }); } // 可选:组件销毁时调用该方法清理订阅 destroy() { this.breakpointSub?.unsubscribe(); } }
额外优化建议
你当前的Service命名MyDataServiceService重复了Service后缀,可以调整为更语义化的名称比如SidenavService,方便后续维护。
内容的提问来源于stack exchange,提问作者Atif Azad
相关产品推荐
相关产品推荐

