三星Galaxy Fold 5打开Angular Material Dialog报错排查
三星Galaxy Fold 5上Angular Material Dialog无法显示的问题排查
今年夏天将企业Web应用从Angular 6升级到Angular 15后,核心用户反馈:在三星Galaxy Fold 5手机上打开特定Angular Material Dialog时,对话框无法显示,报错如下:
TypeError: Cannot read properties of undefined (reading 'focusInitialElementWhenReady') at bt._trapFocus (vendor.68845b091518ea76.js:1:1028636) at bt._openAnimationDone (vendor.68845b091518ea76.js:1:1487405) at bt._onAnimationDone (vendor.68845b091518ea76.js:1:1493261) at vendor.68845b091518ea76.js:1:1494469 at Se (vendor.68845b091518ea76.js:1:1265844) at b (vendor.68845b091518ea76.js:1:1266012) at vendor.68845b091518ea76.js:1:1828893 at z.invoke (polyfills.729dcc77ee327c3e.js:1:6654) at Object.onInvoke (vendor.68845b091518ea76.js:1:1296471) at z.invoke (polyfills.729dcc77ee327c3e.js:1:6594)
该问题仅在三星Galaxy Fold 5上出现,与浏览器无关,且无法在其他设备或模拟器中复现。
对话框组件代码
export class DocumentPropertiesDialogComponent { smShowCheckBoxList: boolean = false; viewModelSubject$: ViewModelSubject<DocumentMetaDataViewModel> = new ViewModelSubject<DocumentMetaDataViewModel>(); readonly documentRights$: Observable<DocumentRightsDTO>; static show(dh: DialogHelper, document: DocumentDTO): Observable<MetaDataUpdateDialogResult> { return dh.showDialog<DocumentPropertiesDialogComponent, DocumentMetaDataDialogData, MetaDataUpdateDialogResult> (DocumentPropertiesDialogComponent, new DocumentMetaDataDialogData(document), { maxHeight: '95vh', maxWidth: '100vw', minHeight: '80vh', xsFullscreen: true, }); } constructor(public dialogRef: MatDialogRef<DocumentPropertiesDialogComponent>, @Inject(MAT_DIALOG_DATA) data: DocumentMetaDataDialogData, public modelCache: ModelCache, public urlFactory: UrlFactory, private _documentMetaDataHelper: DocumentMetaDataHelper, public windowService: WindowService, private _dirtyMetaDataInputHelper: DirtyMetaDataInputHelper, private _documentFunctionService: DocumentFunctionService, private readonly documentService: DocumentService ) { const document = data.document; this.smShowCheckBoxList = !document.metaData || (!document.metaData.assignedDocumentClasses || document.metaData.assignedDocumentClasses.length === 0); document.initializeDocumentMetaDataHelper(this._documentMetaDataHelper); if (data.isUploadDialog && data.document) { data.document.hasChanges.next(true); } this.viewModelSubject$.next(DocumentMetaDataViewModel.createNew(this.modelCache, data.document, data.isUploadDialog, data.documentDepotTypeArchiveAssignments, data.depotTypeID)); this.documentRights$ = this.documentService.getDocumentRights(document.documentID).pipe( shareReplay(1), ); } ... }
DialogHelper代码
@Injectable() export class DialogHelper { private _dialogNumber: number = 0; constructor(public dialogFactory: MatDialog, private ngZone: NgZone, private windowService: WindowService) { } get dialogNumber(): number { return this._dialogNumber; } showDialog<T, D = any, R = any>(componentOrTemplateRef: ComponentType<T> | TemplateRef<T>, data?: D, props?: MatDialogConfig<D> | ShowDialogConfig<D>): Observable<R>;; showDialog<T, D = any, R = any>(componentOrTemplateRef: ComponentType<T> | TemplateRef<T>, data: D, hasBackdrop?: boolean | null, panelClass?: string | null, disableClose?: boolean | null, xsFullScreen?: boolean | null, autofocus?: boolean | null, matDialogConfig?: MatDialogConfig<D> | undefined | null, ): Observable<R>;; showDialog<T, D = any, R = any>(componentOrTemplateRef: ComponentType<T> | TemplateRef<T>, data: D, hasBackdropOrConfig: boolean | MatDialogConfig<D> | ShowDialogConfig<D> | null, panelClass: string | null, disableClose: boolean | null, xsFullScreen: boolean | null, autofocus: boolean | null, matDialogConfig: MatDialogConfig<D> | null): Observable<R>;; showDialog<T, D = any, R = any>(componentOrTemplateRef: ComponentType<T> | TemplateRef<T>, data: D = null, hasBackdropOrConfig: boolean | MatDialogConfig<D> | ShowDialogConfig<D> | null = true, panelClass: string | null = null, disableClose: boolean | null = false, xsFullScreen: boolean | null = false, autofocus: boolean | null = true, matDialogConfig: MatDialogConfig<D> | null = null): Observable<R> { if (hasBackdropOrConfig instanceof MatDialogConfig) { const config: MatDialogConfig<D> = hasBackdropOrConfig; config.data = data; config.closeOnNavigation = true; this._dialogNumber++; return this.ngZone.run(() => this.openDialog(componentOrTemplateRef, config)); } else { const config = matDialogConfig ? matDialogConfig : new MatDialogConfig<D>(); if (typeof hasBackdropOrConfig === 'boolean') { config.hasBackdrop = hasBackdropOrConfig; return this.showDialog<T, D, R>(componentOrTemplateRef, data, config, panelClass, disableClose, xsFullScreen, autofocus, matDialogConfig); } else { Object.assign(config, hasBackdropOrConfig); } if (!this.windowService.size$.value.xsOrLess || xsFullScreen) { config.maxHeight = '100vh'; config.maxWidth = '100vw'; } return this.showDialog<T, D, R>(componentOrTemplateRef, data, config, panelClass, disableClose, xsFullScreen, autofocus, undefined); } } public isDialogOpen(): boolean { return this._dialogNumber > 0; } private openDialog<T, D = any, R = any>(componentOrTemplateRef: ComponentType<T> | TemplateRef<T>, config: MatDialogConfig<D>): Observable<R> { const self = this; return this.dialogFactory.open<T, D, R>(componentOrTemplateRef, config) .afterClosed() .pipe(finalize(() => { self._dialogNumber--; })); } }
package.json核心依赖
"dependencies": { "@angular/animations": "15.2.9", "@angular/cdk": "15.2.9", "@angular/common": "15.2.9", "@angular/compiler": "15.2.9", "@angular/core": "15.2.9", "@angular/elements": "15.2.9", "@angular/forms": "15.2.9", "@angular/material": "15.2.9", "@angular/platform-browser": "15.2.9", "@angular/platform-browser-dynamic": "15.2.9", "@angular/router": "15.2.9", "@material-design-icons/font": "0.14.2", "@ngx-translate/core": "14.0.0", "hammerjs": "2.0.8", "ng-animate": "2.0.1", "ng-circle-progress": "1.5.1", "rxjs": "6.5.4", "tslib": "2.3.0", "zone.js": "0.11.4", ... },
错误原因分析与解决方案
可能原因
- 折叠屏设备视图尺寸异常:三星Galaxy Fold 5的折叠/展开状态下,视图尺寸快速变化可能触发Angular CDK焦点陷阱逻辑异常,
focusInitialElementWhenReady所属的焦点陷阱实例未正确初始化或在动画完成时已被销毁。 - Angular Material 15焦点处理兼容性问题:Angular 15.x的Material组件在折叠屏这类特殊移动设备上存在焦点陷阱的bug,动画完成后实例绑定丢失。
- 对话框配置冲突:
xsFullscreen: true结合折叠屏的特殊尺寸,可能导致对话框DOM元素在动画阶段出现异常,干扰焦点陷阱初始化。
解决方案
禁用对话框自动焦点:在打开对话框的配置中添加
autoFocus: false,绕过焦点陷阱的自动初始化逻辑,避免未定义实例调用:static show(dh: DialogHelper, document: DocumentDTO): Observable<MetaDataUpdateDialogResult> { return dh.showDialog<DocumentPropertiesDialogComponent, DocumentMetaDataDialogData, MetaDataUpdateDialogResult> (DocumentPropertiesDialogComponent, new DocumentMetaDataDialogData(document), { maxHeight: '95vh', maxWidth: '100vw', minHeight: '80vh', xsFullscreen: true, autoFocus: false // 添加此行 }); }升级Angular CDK/Material版本:Angular 15.2.x后续小版本可能修复了折叠屏焦点处理bug,尝试升级到15.x系列的最新稳定版本:
npm install @angular/cdk@15.2.10 @angular/material@15.2.10手动初始化焦点陷阱(备选):如果禁用自动焦点后需要手动设置焦点,可在对话框组件的
ngAfterViewInit钩子中手动创建并激活焦点陷阱:import { FocusTrapFactory } from '@angular/cdk/a11y'; import { ElementRef, ViewChild, AfterViewInit } from '@angular/core'; export class DocumentPropertiesDialogComponent implements AfterViewInit { @ViewChild('focusTarget') focusTarget: ElementRef; constructor(private focusTrapFactory: FocusTrapFactory) {} ngAfterViewInit() { const focusTrap = this.focusTrapFactory.create(this.focusTarget.nativeElement); focusTrap.focusInitialElement(); } }同时在模板中添加标记元素:
<div #focusTarget>...</div>调整对话框尺寸配置:移除
xsFullscreen: true,改用更温和的尺寸适配,避免极端尺寸导致DOM异常:{ maxHeight: '95vh', maxWidth: '95vw', minHeight: '80vh', width: '90vw' }
内容的提问来源于stack exchange,提问作者Florian
相关产品推荐
相关产品推荐

