documentationfor yFiles for HTML 2.6

Commands

yFiles for HTML features a commanding infrastructure very similar in concept to that present in Microsoft’s Windows Presentation Foundation (WPF). This infrastructure provides a flexible mechanism for input handling where a so-called command is used to decouple an input event (resulting from some user interaction) from the actual logic that performs the corresponding action.

The WPF commanding is described here: Commanding Overview (.NET Framework documentation).

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. E.g. editing commands cannot be executed unless GraphEditorInputMode is set as the GraphComponent’s input mode, simply because the implementation for those commands are provided as part of the GraphEditorInputMode.

Commands in yFiles for HTML are of type ICommand 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 ICommand.INCREASE_ZOOM, it is matched by a command binding provided by default by the GraphComponent type itself, i.e. that it is available, no matter whether GraphEditorInputMode or GraphViewerInputMode are used or not.

Executing commands on a target
// 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.
ICommand.INCREASE_ZOOM.execute(null, myGraphComponent)

// Executes the INCREASE_ZOOM command on the GraphControl using the factor 2.
ICommand.INCREASE_ZOOM.execute(2, myGraphComponent)

The predefined commands from the command library can be found as static properties on ICommand.

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.

Predefined command bindings
Class Provided Command Bindings
CanvasComponent
GraphComponent
GraphEditorInputMode
GraphViewerInputModeCommand bindings for selection: SELECT_ITEM, DESELECT_ITEM etc.
NavigationInputModeFor keyboard actions to move/extend the selection and to move the focus indicator.
CreateEdgeInputModeBEGIN_EDGE_CREATION; only available, if the input mode is installed in a CanvasComponent and enabled.

The other dimension of input handling, namely determining whether a command can be executed or not, is taken care of by the combination of command and command binding in the same way.

The following example shows how to test whether a command can be executed on a GraphComponent. With the result of the test you can enable or disable a button in the UI, for example.

Determining availability of a command on a target
// Test whether the INCREASE_ZOOM command on the GraphComponent is available,
// respectively, whether it can be executed with the given parameter value.
const enableButton = ICommand.INCREASE_ZOOM.canExecute(2, myGraphComponent)

Custom Command Bindings

Command bindings can be created programmatically and associated with the GraphComponent with the help of the KeyboardInputMode class.

The following example shows how a new command can be created:

Creating a new command
// first we create a new command implementation
const testCommand = ICommand.createCommand('Test')

Adding a command binding to a GraphComponent can be done with the help of the KeyboardInputMode class:

Adding a command binding to a GraphComponent using KeyboardInputMode
// then the command binding that makes the connection of a command to the respective
// handler functions.
const cb = keyboardInputMode.addCommandBinding(
  testCommand,
  (command, parameter, target) => {
    console.log(`executing ${command.toString()} with ${parameter}`)
    return true
  },
  (command, parameter, target) => parameter !== null
)
// Finally we create key bindings to connect commands and parameters with key gestures
keyboardInputMode.addKeyBinding(Key.K, ModifierKeys.CONTROL, testCommand, 'Go!')
keyboardInputMode.addKeyBinding(Key.K, ModifierKeys.CONTROL | ModifierKeys.SHIFT, testCommand, 'Go Back!')

Alternatively, addKeyBinding can be used to directly add execution handlers to the KeyboardInputMode.

Commands and HTML Widgets

To enable easy usage of Commands in HTML5 web apps, a simple pattern can be applied

Using Commands with Buttons
// create a new CommandAction using the zoom command
const button = document.getElementById('zoomOneToOneButton')
const command = ICommand.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, target) {
  // listen for possible changes
  command.addCanExecuteChangedListener(() => {
    if (command.canExecute(parameter, target)) {
      button.removeAttribute('disabled')
    } else {
      button.setAttribute('disabled', 'disabled')
    }
  })

  // execute the command on click
  button.addEventListener('click', () => {
    if (command.canExecute(parameter, target)) {
      command.execute(parameter, target)
    }
  })

  // set the initial state
  if (!command.canExecute(parameter, target)) {
    button.setAttribute('disabled', 'disabled')
  }
}

// create a new CommandAction using the zoom command
const button = document.getElementById('zoomOneToOneButton') as HTMLButtonElement
const command = ICommand.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: ICommand, parameter: any, target: any): void {
  // listen for possible changes
  command.addCanExecuteChangedListener(() => {
    if (command.canExecute(parameter, target)) {
      button.removeAttribute('disabled')
    } else {
      button.setAttribute('disabled', 'disabled')
    }
  })

  // execute the command on click
  button.addEventListener('click', () => {
    if (command.canExecute(parameter, target)) {
      command.execute(parameter, target)
    }
  })

  // set the initial state
  if (!command.canExecute(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.