Angular中mat-dialog内mat-autocomplete点击外部无法关闭的问题
问题解决:Mat-Dialog内Mat-Autocomplete点击外部无法关闭
问题场景
在Mat-Dialog弹窗内的表单开发中,Mat-Autocomplete下拉框点击弹窗外部(包括弹窗背景)无法自动关闭,但相同实现方式的Mat-Select组件功能完全正常。
组件代码
Autocomplete 实现
<mat-form-field appearance="outline" floatLabel="always"> <mat-label class="input-label">{{ input.label }}</mat-label> <input matInput [type]="input.inputType" [name]="input.name" [placeholder]="input.placeholder" [id]="input.id" [readonly]="input.readOnlyOnAction.includes(formAction)" [formControlName]="input.controlName" [matAutocomplete]="auto" /> <mat-autocomplete #auto="matAutocomplete"> <mat-option *ngFor="let item of input.filteredData" [value]="item[input.dataValue]"> {{ item[input.dataLabel] }} </mat-option> </mat-autocomplete> </mat-form-field>
Select 实现
<mat-form-field appearance="outline" floatLabel="always"> <app-svm-svg-icon-hover-rotate matSuffix svgIcon="chevron" [rotate]="false" [rotateAngle]="180" iconStroke="#0069B4" [rotate]="select.panelOpen" [rotateAngle]="180" (click)="select.panelOpen ? select.close() : select.open()"> </app-svm-svg-icon-hover-rotate> <mat-label class="input-label">{{ input.label }}</mat-label> <mat-select matNativeControl [placeholder]="input.placeholder" [id]="input.id" [formControlName]="input.controlName" #select> <ng-container *ngIf="input.data?.length > 0; else noData"> <mat-option *ngFor="let item of input?.data" [value]="item[input.dataValue]"> {{ item[input.dataLabel] }} </mat-option> </ng-container> <ng-template #noData> <mat-option> No Data </mat-option> </ng-template> </mat-select> </mat-form-field>
原因分析
Mat-Select组件内部已经针对Mat-Dialog场景做了适配,会自动监听弹窗背景点击并关闭下拉面板;而Mat-Autocomplete的默认overlay容器挂载在全局body上,无法感知Mat-Dialog的背景点击事件,导致点击外部时无法触发关闭逻辑。
解决方案
方案一:手动监听外部点击事件关闭面板
通过ViewChild获取Autocomplete实例和输入框元素,监听document点击事件,判断点击区域是否在组件外,手动关闭面板。
- 组件类中添加监听逻辑:
import { Component, ViewChild, ElementRef, HostListener } from '@angular/core'; import { MatAutocomplete } from '@angular/material/autocomplete'; @Component({ selector: 'your-dialog-component', templateUrl: './your-dialog.component.html' }) export class YourDialogComponent { @ViewChild('auto') auto!: MatAutocomplete; @ViewChild('inputElement') inputElement!: ElementRef; @HostListener('document:click', ['$event']) handleClickOutside(event: Event) { const isInsideInput = this.inputElement.nativeElement.contains(event.target); const isInsidePanel = this.auto.panel?.nativeElement?.contains(event.target); if (!isInsideInput && !isInsidePanel && this.auto.isOpen) { this.auto.close(); } } }
- 模板中给input添加模板引用变量:
<input #inputElement matInput [type]="input.inputType" [name]="input.name" [placeholder]="input.placeholder" [id]="input.id" [readonly]="input.readOnlyOnAction.includes(formAction)" [formControlName]="input.controlName" [matAutocomplete]="auto" />
方案二:将Autocomplete的Overlay容器绑定到Dialog容器
修改Autocomplete的overlay挂载目标,让它附加到Mat-Dialog容器内,从而正确感知Dialog的背景点击事件。
在Dialog组件的providers中配置OverlayContainer:
import { Overlay, OverlayContainer } from '@angular/cdk/overlay'; @Component({ selector: 'your-dialog-component', templateUrl: './your-dialog.component.html', providers: [ { provide: OverlayContainer, useFactory: (overlay: Overlay) => { const container = overlay.createContainer(); const dialogContainer = document.querySelector('.mat-dialog-container'); if (dialogContainer) { container.getContainerElement().appendChild(dialogContainer); } return container; }, deps: [Overlay] } ] }) export class YourDialogComponent { // 组件其他逻辑 }
方案三:检查模块导入
确保NgModule中已导入MatAutocompleteModule和OverlayModule:
import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { OverlayModule } from '@angular/cdk/overlay'; @NgModule({ imports: [ // 其他模块 MatAutocompleteModule, OverlayModule ] }) export class YourModule { }
内容的提问来源于stack exchange,提问作者Josip Maričević
相关产品推荐
相关产品推荐

