在 Angular 13 中实现 Monaco 编辑器的最佳选项是什么? 我看过 ngx-monaco-editor,但是最近更新是9个月前,并且升级到 Angular 12,那里的 Monaco 版本是0.20.0(11.02.2020),非常旧 :( 是否有另一种方法在 Angular 13 中使用它?
在 Angular 13 中实现 Monaco 编辑器的最佳选项是什么? 我看过 ngx-monaco-editor,但是最近更新是9个月前,并且升级到 Angular 12,那里的 Monaco 版本是0.20.0(11.02.2020),非常旧 :( 是否有另一种方法在 Angular 13 中使用它?
这是我解决问题的方式,受到atularen/ngx-monaco-editor的启发。但我也不想依赖这个库。可能会有更好的解决方案。
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();
}
}
}
现在需要在编辑器使用前立即调用monacoEditorService.load()(在我的情况下,它是在app.component.ts的构造函数中调用的,以使编辑器始终可用并已预加载)。
现在,您可以根据需要创建编辑器,但请确保在Monaco尚未加载之前不要创建它们。像这样:
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();
}
很可能有比使用布尔标志更优雅的解决方案和这个主题相关。
monaco-editor.component.html
确保组件中有一个像这样的 div:
<div class="editor-container" #editorContainer></div>
any
),除了加载实际的 Monaco 代码的地方。最终我使用了 import * as monacoRuntime from 'monaco-editor'; export var monaco: typeof import('monaco-editor') = monacoRuntime;
以类型安全的方式加载它。但这完全破坏了 Monaco 的布局引擎。我不想依赖于_另一个_包来加载 Monaco。我认为唯一可接受的答案是不依赖第三方包。 - b4ux1t3loader.js
使用require.js
(据我所知)动态地require
Monaco需要的文件,这些文件作为单独的资源/下载提供。问题在于,这实际上背离了捆绑应用程序的目的,并且增加了另一个(隐式和不可管理的)依赖项(require.js
)。并不是要过于消极,只是感觉任何解决方案如果不能导致一个单一的、连贯的Angular捆绑包,最多也只能算是不完整的解决方案。Monaco嵌入在VSCode中,这意味着必须有一种方法可以在不需要向后端发出单独请求的情况下使用它。 - b4ux1t3monacoEditorService.load()
,这是必需的以启动加载。此外,initMonaco
也需要被调用(当然)。编辑器出现了,但看起来很奇怪,因此还需要将 Monaco 样式添加到 angular.json
中。 - a-ctor在这里发布一个答案,使用自定义webpack配置和 Monaco Editor Webpack Loader Plugin 而不是第三方包装库。尚未在 Angular 15+ 中测试,但我不知道为什么这种方法不起作用。我将在接下来的几个月中将我们的应用程序迁移到v15,并在那时更新此帖子以确认该方法是否有效。
npm i -D @angular-builders/custom-webpack monaco-editor-webpack-plugin style-loader css-loader
有很多种方法可以做到这一点,但我选择了使用 TypeScript 并导出默认函数(这样我就可以记录整个配置)。我将其保存在根目录中,以便在 angular.json 中引用。
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
import * as webpack from 'webpack';
export default (config: webpack.Configuration) => {
config?.plugins?.push(new MonacoWebpackPlugin());
// Remove the existing css loader rule
const cssRuleIdx = config?.module?.rules?.findIndex((rule: any) =>
rule.test?.toString().includes(':css')
);
if (cssRuleIdx !== -1) {
config?.module?.rules?.splice(cssRuleIdx!, 1);
}
config?.module?.rules?.push(
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.ttf$/,
use: ['file-loader'],
}
);
return config;
};
"my-application": {
...
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
...
"options": {
"customWebpackConfig": {
"path": "./custom-webpack.config.ts"
},
...
"styles": [
"node_modules/monaco-editor/min/vs/editor/editor.main.css",
"apps/my-application/src/styles.scss"
]
...
}
...
},
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
"options": {
"browserTarget": "my-application:build:development"
}
},
...
import * as monaco from 'monaco-editor';
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
@Component({
selector: 'my-application-editor',
template: `
<div
style="height:100%"
#editorContainer
></div>
`,
styleUrls: ['./editor.component.scss'],
})
export class EditorComponent implements OnInit {
@ViewChild('editorContainer', { static: true }) _editorContainer!: ElementRef;
codeEditorInstance!: monaco.editor.IStandaloneCodeEditor;
constructor() {}
ngOnInit() {
this.codeEditorInstance = monaco.editor.create(this._editorContainer.nativeElement, {
theme: 'vs',
wordWrap: 'on',
wrappingIndent: 'indent',
language: 'typescript',
// minimap: { enabled: false },
automaticLayout: true,
});
}
Webpack插件允许您通过删除您不使用的monaco部分来缩小最终捆绑包的大小。需要记住两件事:
import * as monaco from 'monaco-editor'
也会包含整个库,从而抵消您摇树的努力。这是我们为应用程序使用的内容(将配置对象传递给自定义webpack ts中的MonacoEditorWebpackPlugin):
new MonacoEditorWebpackPlugin({
// a ton of languages are lazily loaded by default, but we dont use any of them
languages: [],
// we can disable features that we end up not needing/using
features: [
'accessibilityHelp',
'anchorSelect',
'bracketMatching',
// 'browser',
'caretOperations',
'clipboard',
// 'codeAction',
// 'codelens',
// 'colorPicker',
// 'comment',
'contextmenu',
'copyPaste',
'cursorUndo',
// 'dnd',
// 'documentSymbols',
// 'dropIntoEditor',
// 'find',
// 'folding',
// 'fontZoom',
'format',
// 'gotoError',
// 'gotoLine',
// 'gotoSymbol',
'hover',
// 'iPadShowKeyboard',
// 'inPlaceReplace',
'indentation',
// 'inlayHints',
'inlineCompletions',
// 'inspectTokens',
'lineSelection',
'linesOperations',
// 'linkedEditing',
// 'links',
// 'multicursor',
// 'parameterHints',
// 'quickCommand',
// 'quickHelp',
// 'quickOutline',
// 'readOnlyMessage',
// 'referenceSearch',
// 'rename',
'smartSelect',
// 'snippet',
'stickyScroll',
// 'suggest',
// 'toggleHighContrast',
'toggleTabFocusMode',
'tokenization',
'unicodeHighlighter',
// 'unusualLineTerminators',
// 'viewportSemanticTokens',
'wordHighlighter',
'wordOperations',
'wordPartOperations',
],
})
相关组件的更��内容如下:
// OLD
// import * as monaco from 'monaco-editor'
// NEW
import { editor, languages } from 'monaco-editor/esm/vs/editor/editor.api';
// OLD
// codeEditorInstance!: monaco.editor.IStandaloneCodeEditor;
// this.codeEditorInstance = monaco.editor.create(...
// NEW
this.codeEditorInstance = editor.create(...
codeEditorInstance!: editor.IStandaloneCodeEditor;
如果像我一样,你正在使用带有预配置 Jest 的 NX,你可能需要在 jest.config.js 中添加 transformIgnorePatterns
参考此答案。
transformIgnorePatterns: ['node_modules/(?!monaco-editor/esm/.*)'],
new MonacoEditorWebpackPlugin({...})
的配置(例如删除语言或功能),在构建或服务时间之前,您可能需要删除.angular/
目录才能看到这些更改的反映。否则,这是一份令人惊叹且详尽的指南@chris-newman! - undefinedAngular的Monaco编辑器
在项目目录中:
npm i --legacy-peer-deps ngx-monaco-editor
npm i --legacy-peer-deps monaco-editor
更新 angular.json 文件
"assets": [
{
"glob": "**/*",
"input": "node_modules/monaco-editor",
"output": "assets/monaco-editor"
},
...
],
component.html
<ngx-monaco-editor [options]="codeEditorOptions" [(ngModel)]="code"></ngx-monaco-editor>
component.ts
Component {
code: string = '';
codeEditorOptions = {
theme: 'vs-dark',
language: 'json',
automaticLayout: true
};
...
}
module.ts
import {MonacoEditorModule} from 'ngx-monaco-editor';
...
@NgModule({
...
imports: [
...,
MonacoEditorModule.forRoot()
...
],
...
})
它对我起作用了 :)
目前原始项目不支持Angular 13。
但是在它的Github Issues页面上,有一个可以与Angular 13一起使用的分支。
问题:
https://github.com/atularen/ngx-monaco-editor/issues/248
发帖人:
dmlukichev
发帖人的分支npm包:
我曾在旧版Angular中使用ngx-monaco-editor。尽管该库非常好,但它并不完全符合我想要使用monaco编辑器库的方式。因此,我为Angular 13编写了一种替代实现,并使用了最新版本的monaco-editor https://github.com/cisstech/nge