Monaco-Editor
С чего начать
Установите monaco-editor следующей командой:
npm install monaco-editor @diasoft/qpalette-monaco-editor --saveПакет называется @diasoft/qpalette-monaco-editor с версии 7.4.11 (и во всей линии 8.x); прежнее
имя @diasoft/ngx-monaco-editor осталось на версии 17.0.0 и больше не обновляется. Если в вашем
package.json ещё старое имя — замените его: при переходе на Q.Palette 8 это делает мигратор
@diasoft/qpalette-cli, на 7.x — вручную, вместе с импортами в коде. Примеры ниже — с новым именем.
Скопируйте monaco-editor в ассеты своей сборки и укажите каталог в baseUrl
MonacoEditorModule.forRoot(): файлов редактора в рутовом приложении с 8.2.0 нет. Два варианта —
по версии @diasoft/qpalette-monaco-editor:
Путь без ведущего assets/ — каталог внутри ассетов вашей сборки, адрес считается сам
(Адреса собственных ассетов).
baseUrl — сам каталог monaco-editor.
angular.json, раздел projects.<project>.architect.build.options.assets:
{
"glob": "**/*",
"input": "node_modules/monaco-editor",
"output": "assets/monaco-editor"
}app.module.ts — forRoot() только в главном модуле, в функциональных — MonacoEditorModule:
import { MonacoEditorModule } from '@diasoft/qpalette-monaco-editor';
@NgModule({
imports: [
BrowserModule,
FormsModule,
MonacoEditorModule.forRoot({ baseUrl: 'monaco-editor' }) // → /api/<сервис>/<компонент>/assets/monaco-editor/min/vs
],
bootstrap: [AppComponent]
})
export class AppModule {}Создайте опции и привязки ngModel редактора в компоненте:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
editorOptions = {theme: 'vs-dark', language: 'javascript'};
code: '''YOUR CODE''';
}Включите редактор в html с опциями и привязками ngModel:
<ngx-monaco-editor [options]="editorOptions" [(ngModel)]="code"></ngx-monaco-editor>Diff-editor
Включите редактор различий в html с опциями и необходимыми привязками:
<ngx-monaco-diff-editor
[options]="options"
[originalModel]="originalModel"
[modifiedModel]="modifiedModel"Стилизация
Чтобы редактор соответствовал высоте контейнера, добавьте height: 100% и заверните редактор в контейнер:
<div style="height: 400px">
<ngx-monaco-editor style="height: 100%" [options]="editorOptions" [(ngModel)]="code"></ngx-monaco-editor>
</div>Добавьте редактору класс, напримерmy-code-editorи добавьте необходимые стили в css/scss файл:
<ngx-monaco-editor
class="my-code-editor"
[options]="editorOptions"
[(ngModel)]="code"
></ngx-monaco-editor>События
<ngx-monaco-editor
[options]="editorOptions"
[(ngModel)]="code"
(onInit)="onInit($event)"
></ngx-monaco-editor>Откуда берётся редактор
Файлы редактора отдаёт та сборка, в ассеты которой их скопировали (запись в angular.json из
С чего начать); каталог задаёт baseUrl, файлы ищутся в
<baseUrl>/min/vs, если baseUrl — сам каталог monaco-editor, иначе — в
<baseUrl>/monaco-editor/min/vs. По умолчанию редактор берётся из рутового приложения, а там его
с 8.2.0 нет, — задайте baseUrl одним из двух способов:
- Путь без ведущего
assets/— каталог внутри ассетов вашей сборки: копия вassets/monaco-editorиbaseUrl: 'monaco-editor'(с 8.2.1; на 8.2.0baseUrl— каталог сmonaco-editor, поэтому там копию кладут в подкаталог:assets/vendor/monaco-editorиbaseUrl: 'vendor'); адрес считается сам, имя сервиса в коде не нужно. Это общее правило Адреса собственных ассетов:assets/…, путь от корня и полный URL используются как есть. - Адрес своей сборки от корня сайта —
'/api/<сервис>/<компонент>/assets'при копии вassets/monaco-editor. Работает на любой версии обёртки; как собрать адрес для веб-компонента и библиотечного проекта — Адрес вручную: версии до 8.2.0.
Куда ведёт каждый вариант — на примере веб-компонента myproduct/catalog:
baseUrl | Каталог vs редактора | Когда |
|---|---|---|
'monaco-editor', './monaco-editor' | /api/myproduct/catalog/assets/monaco-editor/min/vs | с 8.2.1; копия в assets/monaco-editor |
'vendor' | /api/myproduct/catalog/assets/vendor/monaco-editor/min/vs | любая версия; копия в assets/vendor/monaco-editor |
'/api/myproduct/catalog/assets' | /api/myproduct/catalog/assets/monaco-editor/min/vs | любая версия; копия в assets/monaco-editor |
'https://cdn.example.com/editor' | https://cdn.example.com/editor/monaco-editor/min/vs | любая версия; редактор с внешнего адреса |
не задан, 'assets', './assets' | /assets/monaco-editor/min/vs — рутовое приложение | с 8.2.1 — 404, файлов там нет; см. ниже про переходный период. На 8.2.0 без baseUrl редактор ищется в ассетах вашей сборки |
Переходный период. На крупных стендах может стоять UI-микросервис qpassetsui, и тогда
файлы Monaco по адресу рутового /assets/monaco-editor/… отдаёт он (рутовое приложение
перенаправляет к нему с 8.2.1): nginx рутового
перенаправляет к нему запросы, и продукт, который берёт редактор из рутового (без baseUrl
или с 'assets' / './assets' / '/assets'), продолжает работать, ничего не меняя.
Это отсрочка на время миграции, а не замена копии: задайте baseUrl и возите monaco-editor
у себя — на стендах без qpassetsui редактор без этого не откроется.
Первые два варианта в коде:
// angular.json → projects.<project>.architect.build.options.assets
{ "glob": "**/*", "input": "node_modules/monaco-editor", "output": "assets/monaco-editor" }MonacoEditorModule.forRoot({ baseUrl: 'monaco-editor' })Проверьте, откуда ваш редактор берёт файлы
Продукт с baseUrl: 'assets' / './assets' из старых примеров — и, начиная с 8.2.1, без
baseUrl — берёт файлы из рутового приложения, даже если возит свою копию monaco-editor.
Файлов там нет: такой продукт увидит вместо редактора ошибку загрузки loader.js (404) — кроме стендов, где
/assets/monaco-editor/… отдаёт qpassetsui (см. выше). Признак в коде —
MonacoEditorModule.forRoot() пустой либо с baseUrl: 'assets' / './assets'.
- Убедитесь, что в
angular.jsonесть запись копированияnode_modules/monaco-editorв ассеты (С чего начать) — без копии в своей сборке не поможет ничего. - Задайте
baseUrlпо вкладке своей версии из «С чего начать». Адрес от корня сайта не требует обновления пакета — подходит и продукту, который остаётся на прежней версии обёртки.
Монако на странице один: библиотека держит его в window.monaco и, если он уже загружен,
повторно не грузит. Два UI-микросервиса с редактором на одной странице делят копию того, кто
открыл редактор первым, — вместе с его baseUrl и набором языков. Рутовое приложение с 8.2.0
Monaco не использует и в это не вмешивается; на более ранних версиях его окно проксирования
могло загрузить редактор раньше вашего.
Чтобы не сломать редактор соседнему продукту, выкладывайте полную копию monaco-editor —
как в С чего начать, не вырезая языки и воркеры: урезанная копия, загруженная
первой, достанется всем. Если редактор нужен для одного-двух форматов, рассмотрите более лёгкий
CodeMirror — на нём с 8.2.0 работает окно проксирования рутового приложения; механизм изоляции
экземпляров Monaco в Q.Palette пока не реализован.
Конфигурация
МетодforRoot()принимает конфигурацию типаNgxMonacoEditorConfig.
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { MonacoEditorModule, NgxMonacoEditorConfig } from '@diasoft/qpalette-monaco-editor';
import { AppComponent } from './app.component';
const monacoConfig: NgxMonacoEditorConfig = {
baseUrl: 'monaco-editor', // копия в assets/monaco-editor вашей сборки (с 8.2.1); по умолчанию — рутовое
defaultOptions: { scrollBeyondLastLine: false }, // pass default options to be used
onMonacoLoad: () => { console.log((<any>window).monaco); } // here monaco object will be available as window.monaco use this function to extend monaco editor functionalities.
};
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
MonacoEditorModule.forRoot(monacoConfig)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
}Настройка дефолтного JSON
СвойствоonMonacoLoadможет быть использовано для конфигурации дефолтного JSON.
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { MonacoEditorModule, NgxMonacoEditorConfig } from '@diasoft/qpalette-monaco-editor';
import { AppComponent } from './app.component';
export function onMonacoLoad() {
console.log((window as any).monaco);
const uri = monaco.Uri.parse('a://b/foo.json');
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
schemas: [{
uri: 'http://myserver/foo-schema.json',
fileMatch: [uri.toString()],
schema: {
type: 'object',
properties: {
p1: {
enum: ['v1', 'v2']
},
p2: {
$ref: 'http://myserver/bar-schema.json'
}
}
}
}, {
uri: 'http://myserver/bar-schema.json',
fileMatch: [uri.toString()],
schema: {
type: 'object',
properties: {
q1: {
enum: ['x1', 'x2']
}
}
}
}]
});
}
const monacoConfig: NgxMonacoEditorConfig = {
baseUrl: 'monaco-editor', // копия в assets/monaco-editor вашей сборки; по умолчанию — рутовое
defaultOptions: { scrollBeyondLastLine: false },
onMonacoLoad
};
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
MonacoEditorModule.forRoot(monacoConfig)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
}Теперь передайте конфигурацию модели типаNgxEditorModelкомпоненту редактора:
@Component({
selector: 'app-root',
template: `<ngx-monaco-editor [options]="options" [model]="model"></ngx-monaco-editor>`,
styles: []
})
export class AppComponent {
options = {
theme: 'vs-dark'
};
jsonCode = [
'{',
' "p1": "v3",',
' "p2": false',
'}'
].join('\n');
model: NgxEditorModel = {
value: this.jsonCode,
language: 'json',
uri: monaco.Uri.parse('a://b/foo.json')
};
}