У меня есть приложение Angular 17, использующее Angular Material и редактор Angular Monaco v2 . Полную репродукцию можно найти на Stackblitz.
В приложении воспроизведения есть редактор Monaco, привязанный к клавишам CTRL+P. Когда вы нажимаете эту горячую клавишу, должно появиться диалоговое окно, оборачивающее HelloComponent в тот же проект. Это фиктивный компонент с одним текстовым полем, в котором вы можете ввести имя.
Проблема в том, что при открытии диалога его отрисовка почему-то задерживается, вплоть до того, что стили материалов не появляются, а привязка происходит настолько медленно, что ломается.
Я пытался использовать обнаружение изменений отсоединения и повторно прикрепить его после закрытия диалогового окна, как предложено здесь, но ничего не изменилось. Кроме того, компонент внутри диалогового окна очень прост, поэтому у него нет внутренних проблем с производительностью.
Чтобы компонент ввода имени можно было использовать как в качестве «обычного» компонента, так и в качестве компонента, завернутого в диалог материала, он получает дополнительные инъекции в свой конструктор для MatDialogRef (чтобы он мог закрыть диалог, передавая данные обратно) и данные, опционально полученные путем внедрения. через токен MAT_DIALOG_DATA.
Данные имеют тип HelloData, который содержит только строковое свойство name. Во всплывающем окне должно отображаться полученное имя, если таковое имеется, и вы можете его редактировать. Когда вы нажмете на нее, он вернет новое имя.
На стороне компонента контейнера диалоговое окно открывается при нажатии клавиши CTRL+P на базовом экземпляре Monaco путем вызова insertText. Это получает текущий выбранный текст из Монако и открывает диалоговое окно, в котором этот текст передается в качестве редактируемого имени.
После закрытия диалогового окна с помощью кнопки «ОК» компонент контейнера позаботится о замене выделения новым именем, если таковое имеется.
Это полный шаблон компонента:
<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;
}





Похоже, что команды, выполняемые monaco, обрабатываются за пределами 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.
В целом, я бы предположил, что если вы используете оболочку Angular для Монако, она должна обрабатывать подобные вещи, поэтому вам, вероятно, следует создать проблему в соответствующем репозитории git.
Спасибо! Stackblitz теперь работает, хотя я не смог протестировать его на своем реальном приложении, потому что оно все еще страдает от проблемы компиляции, возникшей в самых последних версиях Angular+Monaco. Я тоже попробую там, как только с этим разберутся.