Command pattern
Table of contents
Command basics
The EPiServer user interface makes use of the command pattern to encapsulate interface actions into reusable objects. This allows the logic, for executing an action and whether the application is in a state to execute the action, to be separated away from concrete widgets or objects into its own testable unit. This abstraction means that for one command we could have several UI objects that cause it to execute.
The command base class is epi/shell/command/_Command.
Methods
The command base has a public execute method which executes the action if canExecute is true. This can be called explicitly, but will in general be called by the interface objects associated with the command.
The command base also has two protected methods which should be overridden by implementing classes. The first is _execute, which is call by execute after it has checked that the command is in an executable state. This method is where the executional logic for the command should be defined. The second is _onModelChange, which is called when the model is set on the command. It is intended that this method update the state of canExecute based on the new model object.
Stateful
Commands are stateful so it is possible to watch for changes to their properties. For example, canExecute can be watched; this allows the display state (e.g. enabled/disabled) of interface objects, which represent the command, to be updated when canExecute changes. It also means that the model, on which the command executes or determines it state, can be set using set("model", value). This will cause the protected _onModelChange method to be called on the command, which should in turn cause the state of canExecute to be updated for the new model.
Sample command implementation
define([
"dojo/_base/declare",
"epi/dependency",
"epi/shell/command/_Command"
], function (declare, dependency, _Command) {
return declare([_Command], {
constructor: function () {
// summary:
// Constructs the object and sets up a reference to the content data store.
// tags:
// public
var registry = dependency.resolve("epi.storeregistry");
this.store = registry.get("epi.cms.contentdata");
},
_execute: function () {
// summary:
// Executes this command assuming canExecute has been checked.
// tags:
// protected
var model = this.model;
model.isCommonDraft = true;
return this.store.put(model);
},
_onModelChange: function () {
// summary:
// Updates canExecute after the model has been updated.
// tags:
// protected
var model = this.model,
canExecute = model && !model.isCommonDraft;
this.set("canExecute", canExecute);
}
});
});
Command providers and consumers
A provider and consumer pattern has also been implemented for commands in order to extend the principle of separating concerns to where the commands are used as well.
A common situation when using commands is that the widget or object providing the commands shouldn't be responsible for displaying them or rather the commands it provides need to be shown somewhere else in the interface. For example, the page tree has commands such as create page, translate page, etc. but the page tree shouldn't be responsible for displaying these since it has no knowledge of where or how they should be displayed. Instead the generic component chrome that the page tree resides within is responsible for this and has this knowledge. However the problem is that the page tree has no knowledge of the component chrome. A generic method is need to get the commands from the page tree to the component chrome.
To solve this both epi/shell/command/_CommandProviderMixin> and epi/shell/command/_CommandConsumerMixin have been created.
These two classes provide the link between the commands source and where they should be displayed. Simply, the provider has a list of commands and the consumer has a list of command providers and displays the commands for those providers.
Last updated: Jul 09, 2014