我有一个使用 Angular Material 和 Angular Monaco 编辑器 v2 的 Angular 17 应用程序。请在 Stackblitz 上找到完整的重现。
repro 应用程序有一个绑定到 CTRL+P 的 Monaco 编辑器。当您按下此热键时,会弹出一个对话框,将
HelloComponent
包含在同一项目中。这是一个带有单个文本框输入的虚拟组件,您可以在其中输入名称。
问题是,当打开对话框时,其呈现会出现某种程度的延迟,直到材质样式不出现,并且绑定速度太慢以至于会中断。
我尝试使用分离更改检测并在对话框关闭后重新附加它,如建议的here,但没有任何变化。此外,对话框内的组件非常简单,因此不存在固有的性能问题。
为了使名称输入组件既可用作“普通”组件又可用作材质对话框中包装的组件,它在其构造函数中为
MatDialogRef
进行可选注入(因此它可以关闭传递回数据的对话框)和可选数据通过令牌注入接收MAT_DIALOG_DATA
。
Data 的类型为
HelloData
,仅包含字符串 name
属性。弹出窗口应显示收到的名称(如果有),并让您对其进行编辑。当您单击它时,它将返回新名称。
在容器组件方面,通过调用
insertText
,在底层 Monaco 实例上按按键绑定 CTRL+P 打开对话框。这会从摩纳哥获取当前选定的文本,并打开对话框,将此文本作为可编辑名称传递给它。
单击“确定”关闭对话框后,容器组件将负责用新名称(如果有)替换选择。
这是完整的组件模板:
<div>
<form [formGroup]="form" (submit)="save()">
<mat-form-field>
<input
type="text"
matInput
[formControl]="inputName"
placeholder="name"
/>
</mat-form-field>
<p>Hello, {{ inputName.value }}!</p>
<button mat-flat-button type="submit">OK</button>
</form>
</div>
及其对应代码:
export interface HelloData {
name?: string;
}
@Component({
selector: 'app-hello',
standalone: true,
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
MatButtonModule,
MatFormFieldModule,
MatDialogModule,
MatInputModule,
],
templateUrl: './hello.component.html',
styleUrl: './hello.component.css',
})
export class HelloComponent {
public inputName: FormControl<string | null>;
public form: FormGroup;
constructor(
formBuilder: FormBuilder,
@Optional()
public dialogRef?: MatDialogRef<HelloComponent>,
@Optional()
@Inject(MAT_DIALOG_DATA)
public data?: HelloData
) {
this.inputName = formBuilder.control(data?.name || null);
this.form = formBuilder.group({
name: this.inputName,
});
}
public save(): void {
this.dialogRef?.close(this.inputName.value);
}
}
最后,这是对话框的打开和关闭方式:
private async promptName(name?: string): Promise<string | undefined> {
this._cd.detach();
const dialogRef = this._dialog.open(HelloComponent, {
data: {
name,
},
});
const result: HelloData | undefined = await firstValueFrom(
dialogRef.afterClosed()
);
this._cd.reattach();
this._cd.detectChanges();
return result?.name;
}
看起来摩纳哥执行的命令是在 NgZone 之外处理的,因此它根本不会触发更改检测。由于它位于区域之外,因此手动检查
ChangeDetectorRef
上的更改也不会产生任何结果。这会导致纯 html 被推送到 DOM,而没有由更改检测触发的其他内容。
您应该将命令句柄包含在
ngZone.run
中。注入区域:
constructor(private _dialog: MatDialog, private ngZone: NgZone) {}
然后将处理程序包装在
ngZone.run
中:
this._editor.addCommand(
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyP,
async () => {
this.ngZone.run(async () => await this.insertText());
}
);
这是您的 stackblitz 的工作分支。
总而言之,我假设如果您使用 monaco 的 Angular 包装器,它应该处理此类内容,因此您可能应该在相关的 git 存储库中创建一个问题。