Monaco Wrapper
In all Cadmus projects the Monaco wrapper for Angular has been migrated from from @cisstech/nge (Monaco wrapper + Markdown renderer) to @jean-merelis/ngx-monaco-editor + marked. You should do the same in your project by following this migration guide.
Packages
-
remove
"@cisstech/nge"and install@jean-merelis/ngx-monaco-editor:pnpm uninstall @cisstech/nge pnpm i @jean-merelis/ngx-monaco-editor - set
"monaco-editor"to a version satisfying the installedngx-monaco-editor’s peer dependency. Caret ranges on 0.x versions only allow patch bumps (^0.47.0≡>=0.47.0 <0.48.0), so pin exactly to what the wrapper wants, e.g."^0.47.0". - if required for Markdown preview, add
marked.
Configuration
-
in
app.config, add the Monaco wrapper to theprovidersarray:import { DefaultMonacoLoader, NGX_MONACO_LOADER_PROVIDER, } from '@jean-merelis/ngx-monaco-editor'; export const appConfig: ApplicationConfig = { providers: [ // ... // vendor { provide: NGX_MONACO_LOADER_PROVIDER, useFactory: () => new DefaultMonacoLoader({ paths: { vs: '/vs' } }), }, ] }; -
in angular.json add to
architect/build/options/assets:
{
"glob": "**/*",
"input": "node_modules/monaco-editor/min/vs",
"output": "vs"
}
Usage
Wherever your code uses Monaco:
-
add the wrapper to component’s
imports:import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { EditorInitializedEvent, NgxMonacoEditorComponent, StandaloneEditorConstructionOptions, } from '@jean-merelis/ngx-monaco-editor'; // ... imports: [ // ... NgxMonacoEditorComponent ] -
add component fields:
// Monaco editor private _editor?: StandaloneCodeEditor; public readonly editorOptions: StandaloneEditorConstructionOptions = { minimap: { side: 'right' }, wordWrap: 'on', automaticLayout: true, }; // ... // form control bound to the editor's text public text: FormControl<string>; // handler for editor init public onEditorInit(event: EditorInitializedEvent) { this._editor = event.editor; this._editor.focus(); }The control’s value stays in sync via
[formControl]— there’s no need to manually create or set a Monaco text model. -
add the wrapper to the template:
<div id="editor"> <ngx-monaco-editor [formControl]="text" [language]="'markdown'" [options]="editorOptions" (editorInitialized)="onEditorInit($event)" /> </div> -
add styles: this step is easy to miss and results in an editor that renders at 0×0 (or collapses to a tiny box) even though its container looks correctly sized.
<ngx-monaco-editor>’s host element isdisplay: block; position: relative, and its inner Monaco container isposition: absolute; inset: 0. Absolutely positioned children don’t contribute to their parent’s auto height/width, so the<ngx-monaco-editor>host itself collapses unless it’s given an explicit size. Give the wrapping element a fixed/flex height, and makengx-monaco-editorfill it:
#editor {
height: 600px; /* or flex: 1, or whatever fits your layout */
}
#editor ngx-monaco-editor {
display: block;
width: 100%;
height: 100%;
}
If the editor lives inside something that lazily renders/shows its content (e.g. a
mat-tab),automaticLayout: true(set above ineditorOptions) ensures Monaco re-measures itself once the container becomes visible/resized.
Wiring Brick Plugins
If you also use @myrmidon/cadmus-text-ed (see its README for plugin configuration), bind keyboard shortcuts to plugin selectors in onEditorInit:
private async applyEdit(selector: string) {
if (!this._editor) {
return;
}
const selection = this._editor.getSelection();
const text = selection ? this._editor.getModel()!.getValueInRange(selection) : '';
const result = await this._editService.edit({ selector, text });
this._editor.executeEdits('my-source', [
{
range: selection!,
text: result.text,
forceMoveMarkers: true,
},
]);
}
public onEditorInit(event: EditorInitializedEvent) {
this._editor = event.editor;
this._editor.focus();
if (this._editorBindings) {
Object.keys(this._editorBindings).forEach((key) => {
const n = parseInt(key, 10);
this._editor!.addCommand(n, () => {
this.applyEdit(this._editorBindings![key as any]);
});
});
}
}
Wiring Markdown Preview
For a live Markdown preview alongside the editor, use marked + DomSanitizer rather than a separate Markdown-rendering wrapper:
npm i marked
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { marked } from 'marked';
public readonly previewHtml = signal<SafeHtml>('');
private readonly _sanitizer = inject(DomSanitizer);
private updatePreview(): void {
const html = marked.parse(this.text.value || '', { async: false }) as string;
this.previewHtml.set(this._sanitizer.bypassSecurityTrustHtml(html));
}
Call updatePreview() from text.valueChanges (debounced) and whenever the control’s value is set programmatically:
this.text.valueChanges.pipe(debounceTime(50)).subscribe(() => {
this.updatePreview();
});
Template:
<div class="preview" [innerHTML]="previewHtml()"></div>
If publishing this in an Angular library, add
markedto the library’sdependenciesand toallowedNonPeerDependenciesinng-package.json(it’s a non-Angular runtime dependency).