在 Angular 13 中实现 Monaco 编辑器

Tsv*_*lin 13 monaco-editor angular

在 Angular 13 中实现 Monaco 编辑器的最佳选项是什么?\n我见过 ngx-monaco-editor,但上次更新是 9 个月后,它\xe2\x80\x99s 升级到 Angular 12,Monaco 版本也有 0.20。 0 (11.02.2020),非常旧:(\n是否有另一种方法可以在 Angular 13 中使用它?

\n

mor*_*eli 15

这就是我解决这个问题的方法,很大程度上受到 atularen/ngx-monaco-editor 的启发。但我也不想依赖这种依赖。可能有更好的解决方案。

npm install monaco-editor
Run Code Online (Sandbox Code Playgroud)

角度.json

            "assets": [
              ...
              {
                "glob": "**/*",
                "input": "node_modules/monaco-editor",
                "output": "assets/monaco-editor"
              }
            ],
Run Code Online (Sandbox Code Playgroud)

摩纳哥-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();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,只要您需要编辑器,就调用monacoEditorService.load() (在我的例子中,它在构造函数的 app.component.ts 中调用,以使编辑器始终可用并已预加载)。

现在,您可以根据需要创建编辑器,但请确保在加载 Monaco 之前不要创建它们。像这样:

摩纳哥编辑器.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();
  }
Run Code Online (Sandbox Code Playgroud)

很可能有比布尔标志和这个主题更优雅的解决方案。

摩纳哥-editor.component.html

确保组件中有一个 div,如下所示:

<div class="editor-container" #editorContainer></div>
Run Code Online (Sandbox Code Playgroud)

  • 整体解决方案很棒!我只需要添加一些东西就可以让它工作。在您的示例中,您从未调用启动加载所需的“monacoEditorService.load()”。另外,需要调用“initMonaco”(废话)。编辑器出现但看起来很奇怪,因此有必要将摩纳哥样式添加到“angular.json”中。 (3认同)

Chr*_*man 10

更新于 11/27/23

在这里发布一个答案,该答案使用带有Monaco Editor Webpack Loader Plugin 的自定义 webpack 配置,而不是第 3 方包装器库。使用此方法将现有应用程序迁移到 Angular 15(使用 webpack 5)。

由 codicon.ttf 引起的讨厌的“不允许加载本地资源”错误已通过 webpack 5 加载器配置修复(请参阅步骤 2)并降级css-loader^5.2.7

1)安装依赖

  • 仔细检查摩纳哥版本矩阵
  • npm i -D @angular-builders/custom-webpack monaco-editor-webpack-plugin style-loader css-loader

2) 创建自定义 webpack 配置(基本) - webpack 5 更新

有不止一种方法可以做到这一点,但我选择了打字稿并导出默认函数(这样我就可以控制台记录整个配置)。我将其保存在根目录中,以便在 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'],
    },
    // webpack 4 or lower
    //{
    //  test: /\.ttf$/,
    //  use: ['file-loader'],
    //}

    // webpack 5
    { 
      test: /\.ttf$/,
      type: 'asset/resource'
    }
  );
  return config;
};

Run Code Online (Sandbox Code Playgroud)

3)Angular.json修改

  • 修改architect.build.builder以使用custom-webpack构建器
  • 将 customWebpackConfig 添加到architect.build.builder.options
  • 修改architect.build.builder.options.styles以包含摩纳哥编辑器CSS
  • 更新整个architect.serve块以使用custom-webpack构建器
"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"
      }
    },
    ...
Run Code Online (Sandbox Code Playgroud)

4)现在您可以创建一个编辑器组件

"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"
      }
    },
    ...
Run Code Online (Sandbox Code Playgroud)

5)奖励:优化

webpack 插件允许您通过删除不使用的 monaco 部分来缩小最终包的大小。要记住两件事:

  • 插件配置没有很好的记录(需要进行一些尝试和错误才能确定注释掉的内容是否意外删除了对我们功能至关重要的内容。)
  • 根据文件,您需要非常注意有关摩纳哥的所有进口声明。在我看来,它并没有很好地引起人们对这个细节的注意,但即使是import * as monaco from 'monaco-editor组件或服务中的单个组件也将包含整个库,从而否定了您为树摇动东西所做的努力。

以下是我们最终在应用程序中使用的内容(将配置对象传递给自定义 webpack ts 中的 MonacoEditorWebpackPlugin):

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,
    });
  }
Run Code Online (Sandbox Code Playgroud)

组件中的相关更新为:

  • 更新导入
// OLD
// import * as monaco from 'monaco-editor'
// NEW
import { editor, languages } from 'monaco-editor/esm/vs/editor/editor.api';

Run Code Online (Sandbox Code Playgroud)
  • 更新编辑器创建和打字
// OLD
// codeEditorInstance!: monaco.editor.IStandaloneCodeEditor;
// this.codeEditorInstance = monaco.editor.create(...
// NEW
codeEditorInstance!: editor.IStandaloneCodeEditor;
this.codeEditorInstance = editor.create(...
Run Code Online (Sandbox Code Playgroud)

6) 奖励:Jest 单元测试故障排除

如果像我一样,您使用的是开箱即​​用配置 Jest 的 NX,您可能需要根据此答案transformIgnorePatterns添加到 jest.config.js

transformIgnorePatterns: ['node_modules/(?!monaco-editor/esm/.*)'],