This is how I solved it, heavily inspired by atularen/ngx-monaco-editor. But I also don't want to rely on this dependency. There might be better solutions.
npm install monaco-editor
angular.json:
"assets": [
...
{
"glob": "**/*",
"input": "node_modules/monaco-editor",
"output": "assets/monaco-editor"
}
],
monaco-editor-service.ts:
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class MonacoEditorService {
loaded: boolean = false;
public loadingFinished: Subject<void> = new Subject<void>();
constructor() {}
private finishLoading() {
this.loaded = true;
this.loadingFinished.next();
}
public load() {
// load the assets
const baseUrl = './assets' + '/monaco-editor/min/vs';
if (typeof (<any>window).monaco === 'object') {
this.finishLoading();
return;
}
const onGotAmdLoader: any = () => {
// load Monaco
(<any>window).require.config({ paths: { vs: `${baseUrl}` } });
(<any>window).require([`vs/editor/editor.main`], () => {
this.finishLoading();
});
};
// load AMD loader, if necessary
if (!(<any>window).require) {
const loaderScript: HTMLScriptElement = document.createElement('script');
loaderScript.type = 'text/javascript';
loaderScript.src = `${baseUrl}/loader.js`;
loaderScript.addEventListener('load', onGotAmdLoader);
document.body.appendChild(loaderScript);
} else {
onGotAmdLoader();
}
}
}
Now call monacoEditorService.load(), as soon as you need the editor (in my case it's called in app.component.ts in the constructor, to make the editor always available and already preload it).
Now, you can create editors as you please, but make sure to not create them, before Monaco is loaded yet. Like this:
monaco-editor.component.ts
import ...
declare var monaco: any;
@Component({
selector: 'app-monaco-editor',
templateUrl: './monaco-editor.component.html',
styleUrls: ['./monaco-editor.component.scss'],
})
export class MonacoEditorComponent implements OnInit, OnDestroy, AfterViewInit {
public _editor: any;
@ViewChild('editorContainer', { static: true }) _editorContainer: ElementRef;
private initMonaco(): void {
if(!this.monacoEditorService.loaded) {
this.monacoEditorService.loadingFinished.pipe(first()).subscribe(() => {
this.initMonaco();
});
return;
}
this._editor = monaco.editor.create(
this._editorContainer.nativeElement,
options
);
}
ngAfterViewInit(): void {
this.initMonaco();
}
There are most probably more elegant solutions than a boolean flag and this subject.
monaco-editor.component.html
Make sure, there is a div in the component, like this:
<div class="editor-container" #editorContainer></div>