Angular结合ckeditor5实现富文本以及markdown编辑器

1. 背景介绍

自己编写了一个博客系统,想要支持 html 和 markdown 两种格式,但是又不希望向其他博客系统一样,数据库存两种完全格式的内容,前端用多套编辑器这么麻烦和繁琐。于是做了一下 research 还真让我找到了。

这套方案的核心就是ckeditor5,它原本是一套富文本编辑器,但是当给它添加上 markdown plugin 以后,它就可以变成了一个富文本编辑器+markdown 编辑器类似 typro 编辑器,编辑器的输出统一为了 markdown 格式。

这样做的好处,大致有以下三点:

  1. 无论是偏爱富文本编辑器的用户还是 markdown 文本编辑器的用户都能被满足。非常类似于 typora 的使用体验。
  2. 文档格式统一,当有富文本编辑器偏好的用户习惯了 markdown 的简便性后可以顺利切换到 markdown 编辑器,以前的文档格式不用做转换。
  3. 用户可以采用富文本编辑器书写 markdown 文档,这样在用户不熟悉 markdown 文法的时候,可以使用富文本编辑器中的经验编写 markdown

就目前开源编辑器项目的现状来看,只有 ckeditor5 + markdown 插件支持得最好。以下是国外的一个网友针对 WYSIWYG 系列编辑器做的比较表格。

CKEditor5TinyMCEQuillJSSlateJS
Add ImagesYesYesYesYes
Move ImagesYesYesNo, is too buggyYes, but clunky
Resize ImagesYesYesYes, with plugin “quill-image-resize”No.
Paste ImagesYes“Yes but buggy without paid PowerPaste pluginNo, plugin “quill-image-drop-module” does not work when I try it.Yes with plugin “slate-drop-or-paste-images”
Collaborative EditingYes nativelyYes using Wave.Yes using Wave.Yes using Wave.
TablesYesYesYes, with plugin “quill-better-table”Yes
Paste from OfficeYesYes but buggy without paid PowerPaste pluginNoNo
ListsYesYesYesYes
MarkdownYesNoPartiallyNo

从最后一行可以看出,也只有 ckeditor5 这个富文本编辑器及其插件对 markdown 格式支持最好。我也实际体验了ckeditor-5 Markdown editor demoquilljs-markdown demo.
前者的用户体验要好太多。

所以在这个博客系统中,我果断的放弃了之前的 quill 方案,选用了 ckeditor-5 + markdown 插件(ckeditor5-markdown-gfm)的方案。quill 方案的实例在我的技术博客中也有详细讲解。

2. 创建 Angular 工程

2.1. 创建工程

首先创建一个 angular 工程. 工程的名字就叫 angular-editor.

1
2
3

ng new angular-richtext-markdown

3. 创建 Angular 工程

3.1. 创建工程

首先创建一个 angular 工程. 工程的名字就叫 angular-editor.

1
2
3

ng new angular-editor

3.2. 添加依赖

这里需要添加 @ckeditor/ckeditor5-angular 依赖包, 以下是 ckeditor5-angular 与 Angular 之间的兼容关系.

ckeditor5-angular, angular 以及 ckeditor5 的版本兼容性描述可以在ckeditor5-angular官网找到。

由于我目前使用的 angular 版本 17.3.2, 我选择的 ckeditor5-angular 为 7.x 版本,ckeditor5 版本为 42.x.x, ckeditor5-markdown-gfm 版本为 41.2.1。

1
2
3
npm install --save @ckeditor/ckeditor5-angular@7.0.1
npm install --save @ckeditor/ckeditor5-core @ckeditor/ckeditor5-engine @ckeditor/ckeditor5-utils @ckeditor/ckeditor5-watchdog

安装一个 CKEditor 5 predefined build 或者创建 create a custom predefined build.

1
npm install --save @ckeditor/ckeditor5-build-classic

4. 创建编辑器

4.1. 引入 ckeditor5 模块

添加依赖包之后还不能直接使用 ckeditor, 还需要再使用 ckeditor 的 Module 声明文件引入它.

以下以根模块为例讲解如何引入模块,引入 @ckeditor/ckeditor5-angular 的 CKEditorModule

src/app/app.component.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { Component } from "@angular/core";
import { RouterOutlet } from "@angular/router";
import { CKEditorModule } from "@ckeditor/ckeditor5-angular";
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";

@Component({
selector: "app-root",
standalone: true,
imports: [RouterOutlet, CKEditorModule],
templateUrl: "./app.component.html",
styleUrl: "./app.component.scss",
})
export class AppComponent {
title = "angular";
public Editor = ClassicEditor;
}
1
2
3
<!-- app.component.html -->

<ckeditor [editor]="Editor" data="<p>Hello, world!</p>"></ckeditor>

如何定制编辑器

定制编辑器的方式有很多。

  1. 从源代码构建编辑器

如果您想向现有构建添加更多插件,或者自定义编辑器配置无法控制的内容,则需要首先创建自定义构建,如从源代码构建编辑器指南中所述。

完成以上教程后,您应该会得到一个生成的 ckeditor.js 文件(以及相应的 type 文件)。在下一步中,您应该将其复制到 src 目录中,并将其导入到组件文件中。

  1. 使用 CKEditor 5 online builder 定制

本指南假设您已经使用CKEditor5 在线构建器构建的编辑器创建了一个 zip 档案。

将其解压缩到应用程序的主目录中, 作为源代码的一部分进行使用。

  1. 基于一个已经存在的 predefined build 进行修改

  2. 利用 ckeditor 的核心组件以及一些打包工具从头构建

本文主要讲述如何通过 CKEditor 5 online builder 定制一个支持 markdown 功能的 ckeditor

定制编辑器

导航到CKEditor 5 online builder

  1. 第一步: 选择编辑器类型: classic, Inline, Ballon, Ballon block, Decoupled document.
  2. 第二步: 配置插件
  3. 第三步: 配置工具栏
  4. 第四步: 选择语言
  5. 第五步: 生成 builder 并下载。

对于 markdown editor, 需要选择 source editing 和 markdown 两个插件,后者提供了将输出转化为 markdown 文本,前者是提供了查看和编辑 markdown 的功能。另外需要将一些额外商业 license 的插件删除掉,例如 ckbox。

如何使用定制编辑器

经过前面的步骤我们会获得一个,包含源代码,已经编译后的文件的文件的 zip 文档。

首先将其解压,放置到项目同级的目录下,可以作为独立的代码库,也可可以作为 mono repo 的一个模块进行保存。

然后在 angular 项目安装该依赖。

1
2
3

npm install --save path_to_customized_editor

经过以上操作,我们应该可以在 package.json 中看到如下更改

1
2
3
4
5
"dependencies": {
...
"custom-ckeditor5": "path_to_customized_editor",
...
)

修改 app.component.ts, 将之前的 ckeditor5-build-classic 替换为定制的

src/app/app.component.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { Component } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { RouterOutlet } from "@angular/router";
import { CKEditorModule } from "@ckeditor/ckeditor5-angular";
//import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import Editor from "custom-ckeditor5";

@Component({
selector: "app-root",
standalone: true,
imports: [RouterOutlet, CKEditorModule, FormsModule],
templateUrl: "./app.component.html",
styleUrl: "./app.component.scss",
})
export class AppComponent {
title = "angular";
public Editor = Editor;
htmlData: string = "<p>Hello, world!</p>";
}

重新启动程序,我们应该能看到带有 markdown 功能的编辑器。

如何添加更多插件

如何需要添加更多插件,以及定制的功能,我们只需要维护 online buid 生成的源代码,然后重新编译即可。

例如我们需要添加一个 image upload 的功能。

1
2
3
4
5

cd path_to_customized_editor

npm install --save @ckeditor/ckeditor5-upload

将 SimpleUploadAdapter 添加到插件列表并配置插件

编辑 ckeditor.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

public static override builtinPlugins = [
SimpleUploadAdapter, //将 SimpleUploadAdapter 添加到插件列表
....
];


public static override defaultConfig: EditorConfig = {
toolbar: [ /* ... */ ],
language: 'zh-cn',
simpleUpload: {
// 一些默认配置可以放在这里
// Feature configuration.
}
}

重新编译 cutomized ckeditor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

npm run build

```

在客户端,重新安装cutomized ckeditor


````bash

npm install path_to_customized_editor

```

## 在客户端配置插件

customized ckeditor 可以作为一个可重用的逐渐,在前端或者客户端不同页面可能需要使用不同的参数。

例如对于对于uploadUrl,不同的项目,不同版本,不同的页面可能需要使用url, 这些都可以在具体使用是进行配置。

```json
simpleUpload: {
uploadUrl: 'http://example/project/version/page/upload'
},
```

在创建ckeditor时我们可以给它传入特定的参数。

```html

<ckeditor [editor]="Editor" [config]="customEditorConfig" data="<p>Hello, world!</p>"></ckeditor>

```

例如在使用过程中可以给它指定uploadUrl

```ts

customEditorConfig: EditorConfig = {
licenseKey: this.PRODUCTIVITY_PACK_LICENSE_KEY,
language: 'zh-cn',
toolbar: [
.....
],

],
},
simpleUpload: {
uploadUrl: 'http://example/project/version/page/upload'
},
image: {
toolbar: [
'imageTextAlternative',
]
},
}

```

## 6. 参考文档

[Markdown output](https://ckeditor.com/docs/ckeditor5/latest/features/markdown.html)

[WYSIWYG Comparison](https://docs.google.com/spreadsheets/d/15fVNQKZbO9BstMaiGzlqkCI9-WiKPlo9rWUX-NzNMxU/edit?pli=1&gid=0#gid=0)

[ckeditor-5 Markdown editor demo](https://ckeditor.com/ckeditor-5/demo/markdown/)

[ckeditor5-demos source repository](https://github.com/ckeditor/ckeditor5-demos/tree/master/markdown)

[Angular integration](https://ckeditor.com/docs/ckeditor5/latest/installation/integrations/angular.html)