Contribute to this guide

guideAngular 富文本编辑器组件

npm version

Angular 是一个基于 TypeScript 的开源单页面 Web 应用程序框架。CKEditor 5 的 Angular 组件支持集成不同的编辑器类型。

从该包的 6.0.0 版本开始,您可以使用 CKEditor 5 提供的原生类型定义。查看有关 TypeScript 支持 的详细信息。

# 支持的 Angular 版本

由于 Angular 库输出格式的重大更改,@ckeditor/ckeditor5-angular 包以以下版本发布,以支持各种 Angular 生态系统。

CKEditor 5 Angular 组件版本 Angular 版本 详细信息
积极支持的版本
^9 16+ 迁移到 TypeScript 5。声明文件不向后兼容。需要 CKEditor 5 版本 43 或更高版本。
过去版本(不再维护)
^8 13+ 需要 CKEditor 5 版本 42 或更高版本。
^7 13+ 对等依赖项的更改 (问题)。需要 CKEditor 5 版本 37 或更高版本。
^6 13+ 需要 CKEditor 5 版本 37 或更高版本。
^5 13+ 需要 Angular 版本 13+ 或更高版本。不再维护较低版本。
^5 13+ 需要 Angular 版本 13+ 或更高版本。不再维护较低版本。
^4 9.1+ 需要 CKEditor 5 版本 34 或更高版本。
^3 9.1+ 需要 Node.js 版本 14 或更高版本。
^2 9.1+ 迁移到 TypeScript 4。声明文件不向后兼容。
^1 5.x - 8.x 不再维护的 Angular 版本。

所有可用的 Angular 版本都 列在 npm 上,您可以在其中提取它们。

CKEditor 5 构建器

在我们的交互式构建器中,您可以快速体验 CKEditor 5。它提供了一个易于使用的用户界面,可帮助您配置、预览和下载适合您需求的编辑器。您可以轻松选择

  • 编辑器类型。
  • 您需要的功能。
  • 首选框架 (React、Angular、Vue 或 Vanilla JS)。
  • 首选分发方法。

最后,您将获得适合您需求的即用型代码!

查看我们的交互式构建器

# 快速入门

# 设置项目

本指南假设您已经拥有一个 Angular 项目。要创建这样的项目,您可以使用 Angular CLI。请参考 Angular 文档 了解更多信息。

# 从 npm 安装

首先,安装 CKEditor 5 包

  • ckeditor5 - 包含开源插件和功能的包。
  • ckeditor5-premium-features - 包含高级插件和功能的包。

根据您的配置和选择的插件,您可能需要安装第一个包或两个包。

npm install ckeditor5 ckeditor5-premium-features

然后,安装 适用于 Angular 的 CKEditor 5 WYSIWYG 编辑器组件

npm install @ckeditor/ckeditor5-angular

以下设置因您使用的组件类型而异。

# 独立组件

独立组件提供了一种简化的方式来构建 Angular 应用程序。它们在 Angular 17 中默认启用。独立组件旨在简化设置并减少对 NGModules 的需求。这就是为什么在这种情况下您不需要这样的模块的原因。

相反,将 CKEditorModule 添加到应用程序组件中的导入中。该组件需要将 standalone 选项设置为 true。下面的示例显示了如何在使用开源和高级插件的情况下使用该组件。

// app.component.ts

import { Component, ViewEncapsulation } from '@angular/core';
import { CKEditorModule } from '@ckeditor/ckeditor5-angular';
import { ClassicEditor, Bold, Essentials, Italic, Mention, Paragraph, Undo } from 'ckeditor5';
import { SlashCommand } from 'ckeditor5-premium-features';

@Component( {
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
    encapsulation: ViewEncapsulation.None,
    imports: [ CKEditorModule ],
    standalone: true
} )
export class AppComponent {
    title = 'angular';

    public Editor = ClassicEditor;
    public config = {
        toolbar: [ 'undo', 'redo', '|', 'bold', 'italic' ],
        plugins: [
            Bold, Essentials, Italic, Mention, Paragraph, SlashCommand, Undo
        ],
        licenseKey: '<YOUR_LICENSE_KEY>',
        // mention: {
        //     Mention configuration
        // }
    }
}

根据所使用的插件(仅开源或也包括高级插件),您可能需要导入第一个 CSS 文件或两个 CSS 文件。Angular 默认将样式限定到特定组件。因此,编辑器可能无法检测到附加的样式。您必须将封装选项设置为 ViewEncapsulation.None 以关闭此限定。

/* app.component.css */

@import 'ckeditor5/ckeditor5.css';
@import 'ckeditor5-premium-features/ckeditor5-premium-features.css';

然后,在模板中使用 <ckeditor> 标签运行富文本编辑器

<!-- app.component.html -->

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

# NGModule 组件

如果您想使用 NGModule 组件,请将 CKEditorModule 添加到 imports 数组中。它将使 CKEditor 5 组件在您的 Angular 应用程序中可用。

// app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { CKEditorModule } from '@ckeditor/ckeditor5-angular';

import { AppComponent } from './app.component';

@NgModule( {
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        CKEditorModule
    ],
    providers: [],
    bootstrap: [ AppComponent ]
} )
export class AppModule { }

然后,在您的 Angular 组件中导入编辑器,并将其分配给一个 public 属性,使其从模板中访问。

// app.component.ts

import { Component, ViewEncapsulation } from '@angular/core';
import { ClassicEditor, Bold, Essentials, Italic, Mention, Paragraph, Undo } from 'ckeditor5';
import { SlashCommand } from 'ckeditor5-premium-features';

@Component( {
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: [ './app.component.css' ],
    encapsulation: ViewEncapsulation.None
} )
export class AppComponent {
    title = 'angular';

    public Editor = ClassicEditor;
    public config = {
        toolbar: [ 'undo', 'redo', '|', 'bold', 'italic' ],
        plugins: [
            Bold, Essentials, Italic, Mention, Paragraph, SlashCommand, Undo
        ],
        licenseKey: '<YOUR_LICENSE_KEY>',
        // mention: {
        //     Mention configuration
        // }
    }
}

根据您使用的插件,您可能需要导入第一个 CSS 文件或两个 CSS 文件。Angular 默认将样式限定到特定组件。因此,编辑器可能无法检测到附加的样式。您必须将封装选项设置为 ViewEncapsulation.None 以关闭此限定。

/* app.component.css */

@import 'ckeditor5/ckeditor5.css';
@import 'ckeditor5-premium-features/ckeditor5-premium-features.css';

最后,在模板中使用 <ckeditor> 标签运行富文本编辑器

<!-- app.component.html -->

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

# 支持的 @Input 属性

CKEditor 5 适用于 Angular 的富文本编辑器组件支持以下 @Input 属性

# editor (必填)

提供静态 Editor create() 方法来创建编辑器实例

<ckeditor [editor]="Editor"></ckeditor>

# config

编辑器的 配置

<ckeditor [config]="{ toolbar: [ 'heading', '|', 'bold', 'italic' ] }"></ckeditor>

# data

编辑器的初始数据。它可以是一个静态值

<ckeditor data="<p>Hello, world!</p>"></ckeditor>

或一个共享的父组件的属性

@Component( {
    // ...
} )
export class MyComponent {
    public editorData = '<p>Hello, world!</p>';
    // ...
}
<ckeditor [data]="editorData"></ckeditor>

# tagName

将创建富文本编辑器的 HTML 元素的标签名称。

默认标签是 <div>

<ckeditor tagName="textarea"></ckeditor>

# disabled

控制编辑器的 只读 状态

@Component( {
    // ...
} )
export class MyComponent {
    public isDisabled = false;
    // ...
    toggleDisabled() {
        this.isDisabled = !this.isDisabled
    }
}
<ckeditor [disabled]="isDisabled"></ckeditor>

<button (click)="toggleDisabled()">
    {{ isDisabled ? 'Enable editor' : 'Disable editor' }}
</button>

# watchdog

一个 ContextWatchdog 类的实例,它负责为多个编辑器实例提供相同的上下文,并在发生崩溃时重新启动整个结构。

import CKSource from 'path/to/custom/build';

const Context = CKSource.Context;
const Editor = CKSource.Editor;
const ContextWatchdog = CKSource.ContextWatchdog;

@Component( {
    // ...
} )
export class MyComponent {
    public editor = Editor;
    public watchdog: any;
    public ready = false;

    ngOnInit() {
        const contextConfig = {};

        this.watchdog = new ContextWatchdog( Context );

        this.watchdog.create( contextConfig )
            .then( () => {
                this.ready = true;
            } );
    }
}
<div *ngIf="ready">
    <ckeditor [watchdog]="watchdog"></ckeditor>
    <ckeditor [watchdog]="watchdog"></ckeditor>
    <ckeditor [watchdog]="watchdog"></ckeditor>
</div>

# editorWatchdogConfig

如果未使用 watchdog 属性,则默认情况下将使用 EditorWatchdogeditorWatchdogConfig 属性允许将 配置 传递给该看门狗。

@Component( {
    // ...
} )
export class MyComponent {
    public myWatchdogConfig = {
        crashNumberLimit: 5,
        // ...
    };
    // ...
}
<ckeditor [editorWatchdogConfig]="myWatchdogConfig"></ckeditor>

# disableTwoWayDataBinding

允许禁用双向数据绑定机制。默认值为 false

引入此选项的原因是在大型文档中存在性能问题。默认情况下,在使用 ngModel 指令时,无论何时编辑器的

此选项允许集成者禁用默认行为,并且仅在需要时调用 editor.getData()

# 支持的 @Output 属性

CKEditor 5 适用于 Angular 的富文本编辑器组件支持以下 @Output 属性

# ready

编辑器准备就绪时触发。它对应于 editor#ready
它与编辑器实例一起触发。

请注意,此方法可能会被调用多次。除了初始化之外,它还会在编辑器在崩溃后重新启动时调用。不要在内部保留对编辑器实例的引用,因为它会在重新启动时更改。相反,您应该使用 watchdog.editor

# change

当编辑器的内容发生改变时触发。它对应于 editor.model.document#change:data 事件。
它被触发时,会传递一个包含编辑器和 CKEditor 5 change:data 事件对象的物件。

<ckeditor [editor]="Editor" (change)="onChange($event)"></ckeditor>
import { ClassicEditor } from 'ckeditor5';
import { ChangeEvent } from '@ckeditor/ckeditor5-angular/ckeditor.component';

@Component( {
    // ...
} )
export class MyComponent {
    public Editor = ClassicEditor;

    public onChange( { editor }: ChangeEvent ) {
        const data = editor.getData();

        console.log( data );
    }
    // ...
}

# blur

当编辑器视图失去焦点时触发。它对应于 editor.editing.view.document#blur 事件。
它被触发时,会传递一个包含编辑器和 CKEditor 5 blur 事件数据的物件。

# focus

当编辑器视图获得焦点时触发。它对应于 editor.editing.view.document#focus 事件。
它被触发时,会传递一个包含编辑器和 CKEditor 5 focus 事件数据的物件。

# error

当编辑器崩溃时触发。一旦编辑器崩溃,内部的看门狗机制会重启编辑器并触发 ready 事件。

在 ckeditor5-angular v7.0.1 之前,编辑器初始化期间发生的崩溃不会触发此事件。

#ngModel 集成

该组件实现了 ControlValueAccessor 接口,可以与 ngModel 一起使用。以下是使用方法:

在你的组件中创建一些模型,以供编辑器共享。

@Component( {
    // ...
} )
export class MyComponent {
    public model = {
        editorData: '<p>Hello, world!</p>'
    };
    // ...
}

在模板中使用模型,以启用双向数据绑定。

<ckeditor [(ngModel)]="model.editorData" [editor]="Editor"></ckeditor>

# 样式

用于 Angular 的 CKEditor 5 富文本编辑器组件可以使用组件样式表或全局样式表进行样式化。请参阅如何使用这两种方法设置 CKEditor 5 组件的高度。

# 通过组件样式表设置高度

首先,在父组件的目录中创建一个 (S)CSS 文件,并使用 :host::ng-deep 伪选择器对给定的编辑器部分进行样式化。

/* src/app/app.component.css */

:host ::ng-deep .ck-editor__editable_inline {
    min-height: 500px;
}

然后,在父组件中添加上述样式表的相对路径。

/* src/app/app.component.ts */

@Component( {
    // ...
    styleUrls: [ './app.component.css' ]
} )

# 通过全局样式表设置高度

要使用全局样式表对组件进行样式化,首先创建一个全局样式表。

/* src/styles.css */

.ck-editor__editable_inline {
    min-height: 500px;
}

然后,在 angular.json 配置文件中添加它。

"architect": {
    "build": {
        "options": {
            "styles": [
                { "input": "src/styles.css" }
            ]
        }
    }
}

# 设置占位符

要在主可编辑元素中显示 占位符,请在 CKEditor 5 富文本编辑器组件配置中设置 placeholder 字段。

@Component( {
    // ...
} )
export class MyComponent {
    public config = {
        placeholder: 'Type the content here!'
    }
}

# 访问编辑器实例

CKEditor 5 富文本编辑器组件提供了满足大多数用例所需的功能。当需要访问完整的 CKEditor 5 API 时,可以通过一个额外的步骤来获取编辑器实例。

为此,创建一个指向 <ckeditor> 组件的模板引用变量 #editor

<ckeditor #editor [editor]="Editor"></ckeditor>

然后,使用 @ViewChild( 'editor' ) 装饰的属性获取 <ckeditor> 组件,并在需要时访问编辑器实例。

@Component()
export class MyComponent {
    @ViewChild( 'editor' ) editorComponent: CKEditorComponent;

    public getEditor() {
        // Warning: This may return "undefined" if the editor is hidden behind the `*ngIf` directive or
        // if the editor is not fully initialised yet.
        return this.editorComponent.editorInstance;
    }
}

编辑器的创建是异步的,因此 editorInstance 只有在编辑器创建完成后才会可用。如果你想对刚创建的编辑器进行更改,更好的选择是在 ready 事件中获取 CKEditor 5 实例。

# 如何?

# 使用文档编辑器类型

如果你想使用 文档(解耦)编辑器,你需要 手动将工具栏添加到 DOM

// app.component.ts

import { Component, ViewEncapsulation } from '@angular/core';
import { CKEditorModule } from '@ckeditor/ckeditor5-angular';
import { DecoupledEditor, Essentials, Italic, Paragraph, Bold, Undo } from 'ckeditor5';

@Component( {
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: [ './app.component.css' ],
    encapsulation: ViewEncapsulation.None
    imports: [ CKEditorModule ],
    standalone: true
} )
export class AppComponent {
    title = 'angular';

    public Editor = DecoupledEditor;
    public config = {
        plugins: [ Bold, Essentials, Italic, Paragraph, Undo ],
        toolbar: [ 'undo', 'redo', '|', 'bold', 'italic' ]
    }
    public onReady( editor: DecoupledEditor ): void {
        const element = editor.ui.getEditableElement()!;
        const parent = element.parentElement!;

        parent.insertBefore(
            editor.ui.view.toolbar.element!,
            element
        );
    }
}

导入所需的 CSS 样式表。

/* app.component.css */

@import 'ckeditor5/ckeditor5.css';

然后,在模板中链接该方法。

<!-- app.component.html -->

<ckeditor [editor]="Editor" data="<p>Hello, world!</p>" (ready)="onReady($event)"></ckeditor>

# 使用带有协作插件的编辑器

我们提供了一些在 Angular 应用程序中使用协作编辑的即用型集成

并非必须在上述示例的基础上构建应用程序,但它们可以帮助你入门。

# 本地化

CKEditor 5 支持多种 UI 语言,官方的 Angular 组件也是如此。请按照以下说明在你的 Angular 应用程序中翻译 CKEditor 5。

与 CSS 样式表类似,这两个软件包都有单独的翻译。请按照以下示例导入它们。然后,将它们传递给 config 属性的 translations 数组。

// app.component.ts

import { Component, ViewEncapsulation } from '@angular/core';
import { CKEditorModule } from '@ckeditor/ckeditor5-angular';
import { ClassicEditor } from 'ckeditor5';
// More imports...

import coreTranslations from 'ckeditor5/translations/es.js';
import premiumFeaturesTranslations from 'ckeditor5-premium-features/translations/es.js';

@Component( {
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: [ './app.component.css' ],
    encapsulation: ViewEncapsulation.None
    imports: [ CKEditorModule ],
    standalone: true
} )
export class AppComponent {
    title = 'angular';
    public Editor = ClassicEditor;
    public config = {
        translations: [ coreTranslations, premiumFeaturesTranslations ],
        // More configuration options...
    }
}

有关高级用法,请参阅 设置 UI 语言 指南。

Angular 17 中的本地化存在一个已知问题。请参阅下面的 已知问题部分 了解详情。

# 已知问题

# 模块解析

TypeScript 配置的 moduleResolution 选项决定了从 node_modules 中查找和解析模块的算法。在 Angular 17 中,该选项默认设置为 node。此选项会阻止正确加载编辑器翻译的类型声明。要解决此问题,你可以采取以下几种方法:

  • 你可以将 moduleResolution 选项设置为 bundler。这是 TypeScript 5.0+ 中针对使用打包器的应用程序的推荐设置。这也是解决此问题的推荐方法。你可以查看下面的其他解决方案,以了解较低版本的 TypeScript。
  • 你可以使用 // @ts-expect-error 注释在导入的翻译上方告诉 TypeScript 编译器抑制此问题。
  • 你可以将 Angular 更新到版本 18,其中 moduleResolution 选项默认设置为 bundler
  • 你可以直接从我们的 CDN 导入翻译,例如:import ‘https://cdn.ckeditor.com/ckeditor5/43.3.0/translations/es.umd.js’;。这样,编辑器会自动加载翻译,因此你无需手动将它们传递到配置中。

# Jest 测试

你可以在 Angular 应用程序中使用 Jest 作为测试运行器。不幸的是,Jest 不使用真实的浏览器。相反,它在 Node.js 中运行测试,该测试使用 JSDOM。JSDOM 不是完整的 DOM 实现,虽然它足以满足标准应用程序,但它无法模拟 CKEditor 5 所需的所有 DOM API。

对于测试 CKEditor 5,建议使用利用真实浏览器并提供完整 DOM 实现的测试框架。一些流行的选择包括:

这些框架为测试 CKEditor 5 提供了更好的支持,并更准确地反映了编辑器在真实浏览器环境中的行为。

如果这是不可能的,并且你仍然想使用 Jest,你可以模拟一些所需的 API。以下是如何模拟 CKEditor 5 使用的一些 API 的示例:

beforeAll( () => {
    window.scrollTo = jest.fn();

    window.ResizeObserver = class ResizeObserver {
        observe() {}
        unobserve() {}
        disconnect() {}
    };

    for ( const key of [ 'InputEvent', 'KeyboardEvent' ] ) {
        window[ key ].prototype.getTargetRanges = () => {
            const range = new StaticRange( {
                startContainer: document.body.querySelector( '.ck-editor__editable p' ),
                startOffset: 0,
                endContainer: document.body.querySelector( '.ck-editor__editable p' ),
                endOffset: 0
            } );

            return [ range ];
        };
    }

    Range.prototype.getClientRects = () => ( {
        item: () => null,
        length: 0,
        [ Symbol.iterator ]: function* () {}
    } );
} );

这些模拟应该放在使用 CKEditor 5 的测试之前。它们并不完美,可能无法涵盖所有情况,但它们应该足以满足基本初始化和渲染编辑器。请记住,它们不能替代真正的浏览器测试。

# 贡献和报告问题

用于 Angular 的 CKEditor 5 富文本编辑器组件的源代码在 GitHub 上的 https://github.com/ckeditor/ckeditor5-angular 中提供。