获取和设置数据
在 CKEditor 5 中,有几种方法可以加载和保存数据。在本指南中,您可以了解可用的选项及其优缺点。
# 自动保存
改善用户体验的最佳方法之一是自动保存编辑器数据,因为数据会发生变化。这确保了用户不必记住保存数据,并防止工作丢失。
我们提供自动保存功能,它在需要时自动保存数据(例如发送到服务器)。有关详细信息,请参阅 自动保存 指南。
# 使用数据初始化编辑器
默认情况下,编辑器具有在其初始化的 DOM 元素中的内容。
<div id="editor">
<!-- This content will appear in the editor if you initialize with this element. -->
<p>Hello, world!</p>
</div>
但是,如果您无法更改 HTML 或使用 JavaScript 异步加载数据,可以使用 initialData
配置属性来设置编辑器的初始状态。
ClassicEditor
.create( document.querySelector( '#editor' ), {
plugins: [ /* ... */ ],
toolbar: [ /* ... */ ],
initialData: '<p>Hello, world!</p>'
} )
.then( /* ... */ )
.catch( /* ... */ );
initialData
属性将使用提供的数据初始化编辑器,覆盖 HTML 级别提供的内容。
如果您使用 React 等集成设置编辑器,请查阅文档以获取提供的用于初始化数据的其他属性。
# 使用 getData()
获取编辑器数据
如果您需要按需获取编辑器内容,例如使用 JavaScript API 发送到服务器,则可以使用 getData()
方法。
const data = editor.getData();
为此,您需要存储对 editor
的引用,因为没有全局属性。您可以通过多种方式做到这一点,例如将 editor
分配给在 then()
的回调之外定义的变量
let editor;
ClassicEditor
.create( document.querySelector( '#editor' ), {
plugins: [ /* ... */ ],
toolbar: [ /* ... */ ]
} )
.then( newEditor => {
editor = newEditor;
} )
.catch( error => {
console.error( error );
} );
// Assuming there is a <button id="submit">Submit</button> in your application.
document.querySelector( '#submit' ).addEventListener( 'click', () => {
const editorData = editor.getData();
// ...
} );
getData() 方法可以接受更改返回内容的选项。
# 使用 setData()
替换编辑器数据
在某些情况下,您可能希望按需使用新数据替换编辑器内容。对于此操作,请使用 setData()
方法
editor.setData( '<p>Some text.</p>' );
与获取数据一样,您将需要访问编辑器的实例。请参阅上一节以了解如何保存实例的示例。
# 与 HTML 表单的自动集成
这是集成编辑器的经典方法。它通常用于更简单的 CMS、论坛、评论部分等。
此方法 **仅在经典编辑器中可用**,并且仅在编辑器用于替换 <textarea>
元素时才可用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CKEditor 5 - Classic editor</title>
<link rel="stylesheet" href="https://cdn.ckeditor.com/ckeditor5/43.3.0/ckeditor5.css" />
</head>
<body>
<h1>Classic editor</h1>
<form action="[URL]" method="post">
<textarea name="content" id="editor">
<p>This is some sample content.</p>
</textarea>
<p><input type="submit" value="Submit"></p>
</form>
<script type="importmap">
{
"imports": {
"ckeditor5": "https://cdn.ckeditor.com/ckeditor5/43.3.0/ckeditor5.js",
"ckeditor5/": "https://cdn.ckeditor.com/ckeditor5/43.3.0/"
}
}
</script>
<script type="module">
import {
ClassicEditor,
Essentials,
Paragraph,
Bold,
Italic
} from 'ckeditor5';
ClassicEditor
.create( document.querySelector( '#editor' ), {
plugins: [ Essentials, Paragraph, Bold, Italic ],
toolbar: [ 'bold', 'italic' ]
} )
.catch( error => {
console.error( error );
} );
</script>
</body>
</html>
经典编辑器将在用户提交表单后自动更新 <textarea>
元素的值。您不需要任何其他 JavaScript 代码将编辑器数据发送到服务器。
在您的 HTTP 服务器中,您现在可以从 POST 请求的 content
变量中读取编辑器数据。例如,在 PHP 中,您可以通过以下方式获取它
<?php
$editor_data = $_POST[ 'content' ];
?>
请注意,替换的 <textarea>
元素会在提交之前由 CKEditor 直接自动更新。如果您需要使用 JavaScript 以编程方式访问替换的 <textarea>
的值(例如,在 onsubmit
处理程序中验证输入的数据),那么 <textarea>
元素可能仍然存储原始数据。为了更新替换的 <textarea>
的值,请使用 editor.updateSourceElement()
方法。
如果您需要使用 JavaScript 在任何时候获取 CKEditor 中的实际数据,请使用 editor.getData()
方法,如下一节所述。
当您将数据库中的数据打印到 HTML 页面中的 <textarea>
元素时,您需要对其进行正确编码。例如,如果您使用 PHP,那么一个最小的解决方案看起来像这样
<?php
$data = htmlspecialchars("<p>Hello, world!</p>", ENT_QUOTES, 'UTF-8');
?>
<textarea name="content" id="editor"><?= $data ?></textarea>
因此,<textarea>
将以如下方式打印出来
<textarea><p>Hello, world!</p></textarea>
而不是以如下方式打印
<textarea><p>Hello, world!</p></textarea>
虽然像上面提到的那样简单的内容本身并不需要编码,但对数据进行编码可以防止丢失诸如 <
或 <IMPORTANT>
之类的文本。
# 更新源元素
如果源元素不是 <textarea>
,CKEditor 5 会在编辑器销毁后清除其内容。但是,如果您想使用数据管道输出的内容更新源元素,可以使用 updateSourceElementOnDestroy
配置选项。
ClassicEditor
.create( document.querySelector( '#editor' ), {
// ...
updateSourceElementOnDestroy: true
} );
在配置中启用 updateSourceElementOnDestroy
选项可能会有一些安全隐患,具体取决于您使用的插件。虽然编辑视图是安全的,但数据输出中可能存在一些不安全的内容,因此仅在您了解自己在做什么的情况下才启用此选项。在使用 Markdown、通用 HTML 支持和 HTML 嵌入功能时,尤其要注意。
# 在用户离开页面时发出警报
将编辑器集成到您的网站时,另一个需要考虑的问题是用户可能会在保存数据之前意外离开。这个问题由 自动保存功能 自动处理,但是如果您不使用它而是选择不同的集成方法,您应该考虑处理以下两种情况
- 用户在保存数据之前离开页面(例如,错误地关闭选项卡或单击某个链接)。
- 用户保存了数据,但还有一些挂起的操作,例如上传图像。
为了处理前一种情况,您可以监听本机 window#beforeunload
事件。后一种情况可以使用 CKEditor 5 PendingActions
插件处理。
# 演示
以下示例展示了如何将所有这些机制结合使用以启用或禁用“保存”按钮,并阻止用户在未保存数据的情况下离开页面。
// Note: We need to build the editor from source.
import { ClassicEditor, PendingActions } from 'ckeditor5';
let isDirty = false;
ClassicEditor
.create( document.querySelector( '#editor' ), {
plugins: [
PendingActions,
// ... other plugins
]
} )
.then( editor => {
window.editor = editor;
handleStatusChanges( editor );
handleSaveButton( editor );
handleBeforeunload( editor );
} )
.catch( err => {
console.error( err.stack );
} );
// Handle clicking the "Save" button by sending the data to a
// fake HTTP server (emulated here with setTimeout()).
function handleSaveButton( editor ) {
const saveButton = document.querySelector( '#save' );
const pendingActions = editor.plugins.get( 'PendingActions' );
saveButton.addEventListener( 'click', evt => {
const data = editor.getData();
// Register the action of saving the data as a "pending action".
// All asynchronous actions related to the editor are tracked like this,
// so later on you only need to check `pendingActions.hasAny` to check
// whether the editor is busy or not.
const action = pendingActions.add( 'Saving changes' );
evt.preventDefault();
// Save the data to a fake HTTP server.
setTimeout( () => {
pendingActions.remove( action );
// Reset isDirty only if the data did not change in the meantime.
if ( data == editor.getData() ) {
isDirty = false;
}
updateStatus( editor );
}, HTTP_SERVER_LAG );
} );
}
// Listen to new changes (to enable the "Save" button) and to
// pending actions (to show the spinner animation when the editor is busy).
function handleStatusChanges( editor ) {
editor.plugins.get( 'PendingActions' ).on( 'change:hasAny', () => updateStatus( editor ) );
editor.model.document.on( 'change:data', () => {
isDirty = true;
updateStatus( editor );
} );
}
// If the user tries to leave the page before the data is saved, ask
// them whether they are sure they want to proceed.
function handleBeforeunload( editor ) {
const pendingActions = editor.plugins.get( 'PendingActions' );
window.addEventListener( 'beforeunload', evt => {
if ( pendingActions.hasAny ) {
evt.preventDefault();
}
} );
}
function updateStatus( editor ) {
const saveButton = document.querySelector( '#save' );
// Disables the "Save" button when the data on the server is up to date.
if ( isDirty ) {
saveButton.classList.add( 'active' );
} else {
saveButton.classList.remove( 'active' );
}
// Shows the spinner animation.
if ( editor.plugins.get( 'PendingActions' ).hasAny ) {
saveButton.classList.add( 'saving' );
} else {
saveButton.classList.remove( 'saving' );
}
}
如何理解此演示
- 当数据正在发送到服务器或有任何其他挂起的操作(例如,正在上传图像)时,按钮将变为“正在保存...”。
- 如果您正在上传图像或数据尚未成功保存,系统会询问您是否要离开页面。您可以通过将大型图像拖放到编辑器中或将“HTTP 服务器延迟”更改为较高的值(例如 9000 毫秒)并单击“保存”按钮来测试这一点。这些操作将使编辑器“忙”更长时间 - 然后尝试离开页面。
更改此编辑器的内容,然后将其保存在服务器上。
服务器数据
<p>Change the content of this editor, then save it on the server.</p>
我们每天都在努力使我们的文档保持完整。您是否发现过时信息?是否缺少内容?请通过我们的 问题跟踪器 报告。
随着 42.0.0 版的发布,我们改写了许多文档以反映新的导入路径和功能。我们感谢您的反馈,这将帮助我们确保文档的准确性和完整性。