category: builds-integration
{@snippet builds/saving-data/build-autosave-source}
CKEditor 5 allows you to retrieve and save the data to your server (or to your system in general) in various ways. In this guide you can learn about the options and their pros and cons.
This is the classical way of integrating the editor. It is typically used in simpler CMSes, forums, comment sections, etc.
This approach is only available in the {@link builds/guides/overview#classic-editor Classic editor} and only if it was used to replace a <textarea> element:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CKEditor 5 - Classic editor</title>
<script src="https://cdn.ckeditor.com/ckeditor5/{@var ckeditor5-version}/classic/ckeditor.js"></script>
</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>
ClassicEditor
.create( document.querySelector( '#editor' ) )
.catch( error => {
console.error( error );
} );
</script>
</body>
</html>
````
The Classic editor will automatically update the value of the `<textarea>` element once the user submits the form. You do not need any additional JavaScript code to sent the editor data to the server.
In your HTTP server, you can now read the editor data from the `content` variable of the POST request. For instance, in PHP, you can get them this way:
```php
<?php
$editor_data = $_POST[ 'content' ];
?>
Please note that the replaced `<textarea>` element is updated automatically by CKEditor straight before the submission. If you need to access the `<textarea>` value programmatically with JavaScript (e.g. in the `onsubmit` handler to validate the entered data), there is a chance that the `<textarea>` element would still store the original data. In order to update the value of replaced `<textarea>`, use the {@link module:editor-classic/classiceditor~ClassicEditor#updateSourceElement `editor.updateSourceElement()`} method.
If you need to get the actual data from CKEditor at any moment using JavaScript, use the {@link module:editor-classic/classiceditor~ClassicEditor#getData `editor.getData()`} method as described in the next section.
When you use AJAX requests instead of the classical integration with HTML forms, implement a single-page application or you use a different editor type than the Classic editor (and hence, you cannot use the previous method), you can retrieve the data from the editor by using the {@link module:editor-classic/classiceditor~ClassicEditor#getData editor.getData()} method.
For that, you need to store the reference to the editor because, unlike in CKEditor 4, there is no global CKEDITOR.instances property. You can do that in multiple ways, e.g. assigning the editor to a variable defined outside the then()'s callback:
let editor;
ClassicEditor
.create( document.querySelector( '#editor' ) )
.then( newEditor => {
editor = newEditor;
} )
.catch( error => {
console.error( error );
} );
// Assuming there's a <button id="submit">Submit</button> in your application.
document.querySelector( '#submit' ).addEventListener( 'click', () => {
const editorData = editor.getData();
// ...
} );
The {@link module:autosave/autosave~Autosave} allows you to automatically save the data (e.g. send it to the server) when needed (when the user changed the content).
This plugin is unavailable in any of the builds by default so you need to {@link builds/guides/development/installing-plugins install it}.
Assuming that you implemented a saveData() function which sends the data to your server and returns a promise which is resolved once the data is successfully saved, configuring the autosave feature is as simple as:
ClassicEditor
.create( document.querySelector( '#editor' ), {
plugins: [
Autosave,
// ... other plugins
],
autosave: {
save( editor ) {
return saveData( editor.getData() );
}
},
// ... other configuration options
} );
The autosave feature listens to the {@link module:engine/model/document~Document#event:change:data editor.model.document#change:data} event, throttles it and executes the config.autosave.save() function.
It also listens to the native window#beforeunload event and blocks it in the following cases:
save() function did not resolve its promise or it was not called yet due to throttling),This automatically secures you from the user leaving the page before the content is saved or some ongoing actions like image upload did not finish.
This demo shows a simple integration of the editor with a fake HTTP server (which needs 1000ms to save the content).
ClassicEditor
.create( document.querySelector( '#editor' ), {
autosave: {
save( editor ) {
return saveData( editor.getData() );
}
}
} )
.then( editor => {
window.editor = editor;
displayStatus( editor );
} )
.catch( err => {
console.error( err.stack );
} );
// Save the data to a fake HTTP server.
function saveData( data ) {
return new Promise( resolve => {
setTimeout( () => {
console.log( data );
resolve();
}, HTTP_SERVER_LAG );
} );
}
function displayStatus( editor ) {
const pendingActions = editor.plugins.get( 'PendingActions' );
const statusIndicator = document.querySelector( '#editor-status' );
pendingActions.on( 'change:hasAny', ( evt, propertyName, newValue ) => {
if ( newValue ) {
statusIndicator.classList.add( 'busy' );
} else {
statusIndicator.classList.remove( 'busy' );
}
} );
}
How to understand this demo:
save()'s promise was not resolved).{@snippet builds/saving-data/autosave}
The additional concern when integrating the editor in your website is that the user may mistakenly leave before saving the data. This problem is automatically handled by the autosave feature described above, but if you do not use it and instead chose different integration methods, you should consider handling these two scenarios:
To handle the former situation you can listen to the native window#beforeunload event. The latter situation can be handled by using CKEditor 5's {@link module:core/pendingactions~PendingActions} plugin.
The below example shows how all these mechanism can be used together to enable/disable a "Save" button and blocking the user from leaving the page without saving the data.
let isDirty = false;
ClassicEditor
.create( document.querySelector( '#editor' ) )
.then( editor => {
window.editor = editor;
handleStatusChanges( editor );
handleSaveButton( editor );
handleBeforeunload( editor );
} )
.catch( err => {
console.error( err.stack );
} );
// Handle clicking the "Save" button.
function handleSaveButton( editor ) {
const saveButton = document.querySelector( '#save' );
const pendingActions = editor.plugins.get( 'PendingActions' );
saveButton.addEventListener( 'click', evt => {
const data = editor.getData();
const action = pendingActions.add( 'Saving changes' );
evt.preventDefault();
// Fake HTTP server's lag.
setTimeout( () => {
log( data );
pendingActions.remove( action );
// Reset isDirty only if data didn't change in the meantime.
if ( data == editor.getData() ) {
isDirty = false;
}
updateStatus( editor );
}, HTTP_SERVER_LAG );
} );
}
function handleStatusChanges( editor ) {
const pendingActions = editor.plugins.get( 'PendingActions' );
pendingActions.on( 'change:hasAny', () => updateStatus( editor ) );
editor.model.document.on( 'change:data', () => {
isDirty = true;
updateStatus( editor );
} );
}
function handleBeforeunload( editor ) {
const pendingActions = editor.plugins.get( 'PendingActions' );
window.addEventListener( 'beforeunload', evt => {
if ( pendingActions.hasAny ) {
evt.returnValue = pendingActions.first.message;
}
} );
}
function updateStatus( editor ) {
const saveButton = document.querySelector( '#save' );
if ( isDirty ) {
saveButton.classList.add( 'active' );
} else {
saveButton.classList.remove( 'active' );
}
if ( editor.plugins.get( 'PendingActions' ).hasAny ) {
saveButton.classList.add( 'saving' );
} else {
saveButton.classList.remove( 'saving' );
}
}
How to understand this demo:
{@snippet builds/saving-data/manualsave}