Commands
# Commands purpose
Now that we have the data conversion sorted out, let’s register a highlight
command. Commands encapsulate logic that can be executed by other plugins or from the user interface, for example by clicking a button in the editor’s toolbar.
# Registering a command
Let’s create a new HighlightCommand
class below the Highlight
function and add the necessary import in src/plugin.js
:
import { Command } from 'ckeditor5';
class HighlightCommand extends Command {
refresh() {
// Handle state.
}
execute() {
// Command logic.
}
}
Then at the bottom of the Highlight
function, add the following code to register the command:
editor.commands.add( 'highlight', new HighlightCommand( editor ) );
Our command class has two methods:
refresh()
, which handles the state.execute()
, which handles the logic.
# Command state
Our plugin only allows text highlighting, so when only an image or table is selected in the editor, we should disable the command. We also want to remove highlighting if the command is called and the selected text is already highlighted.
For this reason, we need a state that indicates whether selections in the editor can be highlighted and whether they are already highlighted.
The command state is managed by the refresh()
method. This method is called whenever the model is updated, ensuring that the state is refreshed and always up-to-date.
Let’s implement the refresh()
method to update two command properties:
isEnabled
, which indicates whether highlighting is allowed on the current selection,value
, which indicates whether the selection is already highlighted.
refresh() {
const { document, schema } = this.editor.model;
// Check if selection is already highlighted.
this.value = document.selection.getAttribute( 'highlight' );
// Check if command is allowed on current selection.
this.isEnabled = schema.checkAttributeInSelection( document.selection, 'highlight' );
}
# Command logic
Now that we have the necessary state, we can add logic for updating the model.
Let’s update the execute()
method like this:
execute() {
const model = this.editor.model;
const selection = model.document.selection;
const newValue = !this.value;
model.change( ( writer ) => {
if ( !selection.isCollapsed ) {
const ranges = model.schema.getValidRanges( selection.getRanges(), 'highlight' );
for ( const range of ranges ) {
if ( newValue ) {
writer.setAttribute( 'highlight', newValue, range );
} else {
writer.removeAttribute( 'highlight', range );
}
}
}
if ( newValue ) {
return writer.setSelectionAttribute( 'highlight', true );
}
return writer.removeSelectionAttribute( 'highlight' );
} );
}
All changes to the model are made using the model writer. Its instance is available in the callback passed to the model.change()
method, so we will use it.
In the callback, we first check if the selection is collapsed. Unlike a standard selection, which can span multiple letters, elements, or even blocks, a collapsed selection has a range of zero, meaning that it starts and ends at the same position. In other words, the collapsed selection is just a caret.
If we are dealing with a standard (not collapsed) selection, we check for all the ranges where the highlight
attribute can be used, loop over them, and either add or remove this attribute depending on the current state.
If the selection is collapsed, we either add or remove the attribute based on the current state.
# Testing changes
Let’s test our changes. In the browser, select some of the text in the editor. Then open a console and run the following code:
editor.execute( 'highlight' );
If everything went well, the text you selected should be highlighted in the editor.
In the CKEditor Inspector, open the Commands
tab to see all available commands. The highlight
command should also be listed.
# What’s next
If you want to read more about the commands, see the Commands document.
Otherwise go to the next chapter, where you will learn more about updating the model UI.
Every day, we work hard to keep our documentation complete. Have you spotted outdated information? Is something missing? Please report it via our issue tracker.
With the release of version 42.0.0, we have rewritten much of our documentation to reflect the new import paths and features. We appreciate your feedback to help us ensure its accuracy and completeness.