Commands
yFiles for HTML features a commanding infrastructure similar to that in other modern UI frameworks. This infrastructure provides a flexible mechanism for input handling. A command decouples an input event (resulting from an user interaction) from the actual logic that performs the corresponding action.
Commands effectively separate the invocation, interaction, and implementation from each other. A command is just an abstract concept of an interaction to perform, e.g. increase the zoom level, or copy to clipboard. The actual implementation for that command lies with the component it is invoked on as target. Commands are connected to their actual implementation via a command binding.
This also means that commands can become available or unavailable depending on how the GraphComponent is configured. For example, editing commands cannot be executed unless GraphEditorInputMode is set as the GraphComponent's input mode, because the implementation for those commands is part of the GraphEditorInputMode.
Commands in yFiles for HTML are of type Command and the command are managed by KeyboardInputMode.
The Executing commands on a target example shows how commands can be executed on a GraphComponent to increase the zoom level. The command is the Command.INCREASE_ZOOM, and it is matched by a command binding provided by default by the GraphComponent class.
// Executes the INCREASE_ZOOM command on the GraphComponent.
// The parameter value given is optional and not provided, here, which means that a default value is
// chosen as the factor.
myGraphComponent.executeCommand(Command.INCREASE_ZOOM)
// Executes the INCREASE_ZOOM command on the GraphControl using the factor 2.
myGraphComponent.executeCommand(Command.INCREASE_ZOOM, 2)
The Predefined commands table shows the predefined commands from the command library classes and GraphComponent view implementation classes:
Class | Commands |
---|---|
Default command bindings are commonly provided by the GraphComponent (some of them by inheritance), but further bindings are added by several input modes like GraphEditorInputMode, GraphViewerInputMode, etc.
The following table lists some of the predefined command bindings.
Class | Provided Command Bindings |
---|---|
| |
| |
| |
The other aspect of input handling, namely determining whether a command can be executed, is handled by the combination of command and command binding.
The following example shows how to test whether a command can be executed on a GraphComponent. You can use the result of this test to enable or disable a button in the UI, for example.
// Test whether the INCREASE_ZOOM command on the GraphComponent is available,
// respectively, whether it can be executed with the given parameter value.
const enableButton = myGraphComponent.canExecuteCommand(
Command.INCREASE_ZOOM,
2
)
Custom Command Bindings
Command bindings can be created programmatically and associated with the GraphComponent using the KeyboardInputMode class.
Adding a command binding to a GraphComponent can be done with the help of the KeyboardInputMode class:
// first create a handler function.
const logCommand = (command) => {
console.log(command)
}
// Then, we create key bindings to connect the handler function with key gestures
keyboardInputMode.addKeyBinding('K', ModifierKeys.CONTROL, () =>
logCommand('Go!')
)
keyboardInputMode.addKeyBinding(
'K',
ModifierKeys.CONTROL | ModifierKeys.SHIFT,
() => logCommand('Go Back!')
)
// first create a handler function.
const logCommand = (command: string): void => {
console.log(command)
}
// Then, we create key bindings to connect the handler function with key gestures
keyboardInputMode.addKeyBinding('K', ModifierKeys.CONTROL, () =>
logCommand('Go!')
)
keyboardInputMode.addKeyBinding(
'K',
ModifierKeys.CONTROL | ModifierKeys.SHIFT,
() => logCommand('Go Back!')
)
Commands and HTML Widgets
To enable easy usage of Commands in web apps, a simple pattern can be applied
// create a new CommandAction using the zoom command
const button = document.getElementById('zoomOneToOneButton')
const command = Command.ZOOM
const parameter = 1
const target = myGraphComponent
// use our helper function to bind the button to the command
bindCommand(button, command, parameter, target)
// noinspection JSAnnotator
function bindCommand(button, command, parameter, targetCanvas) {
// listen for possible changes
targetCanvas.addEventListener('can-execute-changed', () => {
if (targetCanvas.canExecuteCommand(command, parameter)) {
button.removeAttribute('disabled')
} else {
button.setAttribute('disabled', 'disabled')
}
})
// execute the command on click
button.addEventListener('click', () => {
if (targetCanvas.canExecuteCommand(command, parameter)) {
targetCanvas.executeCommand(command, parameter)
}
})
// set the initial state
if (!targetCanvas.canExecuteCommand(parameter, target)) {
button.setAttribute('disabled', 'disabled')
}
}
// create a new CommandAction using the zoom command
const button = document.getElementById(
'zoomOneToOneButton'
) as HTMLButtonElement
const command = Command.ZOOM
const parameter = 1
const target = myGraphComponent
// use our helper function to bind the button to the command
bindCommand(button, command, parameter, target)
// noinspection JSAnnotator
function bindCommand(
button: HTMLButtonElement,
command: Command,
parameter: any,
targetCanvas: CanvasComponent
): void {
// listen for possible changes
targetCanvas.addEventListener('can-execute-changed', () => {
if (targetCanvas.canExecuteCommand(command, parameter)) {
button.removeAttribute('disabled')
} else {
button.setAttribute('disabled', 'disabled')
}
})
// execute the command on click
button.addEventListener('click', () => {
if (targetCanvas.canExecuteCommand(command, parameter)) {
targetCanvas.executeCommand(command, parameter)
}
})
// set the initial state
if (!targetCanvas.canExecuteCommand(parameter, target)) {
button.setAttribute('disabled', 'disabled')
}
}
With this setup in place, clicking the widget finds the corresponding logic to execute the command on the target and invokes it as described above. Furthermore, the button will automatically be enabled or disabled depending on whether the command can currently be executed or not.
Command bindings can also be added using KeyboardInputMode's method addCommandBinding. Using the KeyboardInputMode has the advantage that commands are automatically disabled when the input mode is disabled, e.g. during a layout animation.