Creating an Editor Widget
Table of Contents
Introduction
The EPiServer page editing is based on the Dojo client script toolkit. Each property, or block that is to be edited consists of one or several widgets. A widget is a Dojo JavaScript class that can create a user interface. This example shows how to create a simple editing widget.
You can click here to see the complete source code for the widget used in this example.
Widget Overview
The following properties are mixed into the widget when it is constructed. Further properties can be mixed in specifically for a particular widget by using a PropertyEditorDescriptor.
Property | Description |
---|---|
intermediateChanges | Indicates whether onChange is fired for each value change or only on demand. |
label | The title of the property to be edited. |
value | The current value of the widget. |
required | Indicates whether this widget should require a value. |
The following methods will be used by the wrapper displaying this widget if they are provided.
Method | Description |
---|---|
onChange | Callback event that should be raise from within the widget when the value has changed. The wrapper displaying this widget will listen to this event and update the UI when it occurs. |
focus | Called when the widget is displayed, should give focus to the start element in the widget. |
isValid | Called during validation when item is saving, true if the current value is valid. |
Creating a Simple Widget
In this example we'll create a simple widget for entering valid email addresses. In order to create a widget which can be used in a dialog editor you must inherit from the dijit._widget class. See the code snippet below for the basic structure of a widget class.
define("acme.widget.EmailTextbox", [
"dojo/_base/declare",
"dijit/_Widget",
"dijit/_TemplatedMixin"
], function (
declare,
_Widget,
_TemplatedMixin) {
declare("acme.widget.EmailTextbox", [_Widget, _TemplatedMixin], {
// templateString: [protected] String
// A string that represents the default widget template.
templateString: '<div> \
<input type="email" data-dojo-attach-point="email" data-dojo-attach-event="onchange:_onChange" /> \
</div>'
})
});
When this widget is created by the editor the initial value will be passed in as a constructor arguement with the property name value. This property will be automatically mixed into the widget by the constructor in the dijit._widget class. To make sure that the value is updated in the textbox when it is set on the widget we can declare a _setValueAttr method; this method will be called when we call set('value', value).
postCreate: function () {
// summary:
// Set the value to the textbox after the DOM fragment is created.
// tags:
// protected
this.set('value', this.value);
if (this.intermediateChanges) {
this.connect(this.email, 'onkeydown', this._onIntermediateChange);
this.connect(this.email, 'onkeyup', this._onIntermediateChange);
}
},
_setValueAttr: function (value) {
// summary:
// Sets the value of the widget to "value" and updates the value displayed in the textbox.
// tags:
// private
this._set('value', value);
this.email.value = this.value || '';
}
The set method also references a variable named email. This is the textbox DOM node and it is automatically assigned to this variable name by the dijit._Templated class when it parses the data-dojo-attach-point in the template code.
When the changes made in the widget need to be populated to the page we can call the onChange method passing it the new value. This should be called as often as necessary during the editing in order to give the editor an accurate preview of the changes they are making.
_onChange: function (event) {
// summary:
// Handles the textbox change event and populates this to the onChange method.
// tags:
// private
this._set('value', event.target.value);
this.onChange(this.value);
}
This _onChange method is a private event handler that is triggered when a change is made on the textbox. This is wired up in the template using the data-dojo-attach-event syntax. Once it has updated the value it will call the onChange method casuing a page update.
We want to be able to support intermediate changes; this is where we update the UI as soon as the user makes any sort of update. For this example, because we are dealing with a textbox, we want to update the UI when the user starts typing. To do this, in the postCreate method, we've connected to the onKeyDown and onKeyUp events on the textbox element if the intermediateChanges property is set to true.
_onIntermediateChange: function (event) {
// summary:
// Handles the textbox key press events event and populates this to the onChange method.
// tags:
// private
if (this.intermediateChanges) {
this._set('value', event.target.value);
this.onChange(this.value);
}
}
You can also control the where the focus is set when the control loads by implementing the focus method.
focus: function () {
// summary:
// Put focus on this widget.
// tags:
// public
dijit.focus(this.email);
}
Validation
In order to support validation the widget must implement the isValid method.
isValid: function () {
// summary:
// Indicates whether the current value is valid.
// tags:
// public
var emailRegex = '[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+';
if (!this.required) {
emailRegex = '(' + emailRegex + ')?';
}
var regex = new RegExp('^' + emailRegex + '$');
return regex.test(this.value);
}
The constraints for a property are also mixed into the widget when it is constructed. For example, if the value has the required checkbox ticked in admin mode then that will be passed through as the property required.
Dealing with Child Dialogs
If your widget needs to launch a dialog of its own, then you will need to extend an additional class and set a few property values in the correct place in order to ensure that the blur event, going from the main dialog to the new one, doesn't close the main dialog. The class that you need to extend is called epi.cms.widget._HasChildDialogMixin and it provides one additional property called isShowingChildDialog.
This property is used in the blur event of the main dialog to determine whether it should hide itself. Therefore if we want to stop the main dialog from being hidden when the widget launches a child dialog we need to set the value to true before lauching the child dialog. We can then set it back to false once the child dialog has closed.
_showChildDialog: function () {
var dialog = dijit.Dialog({
title: 'Child Dialog'
});
this.connect(dialog, 'onHide', this._onHide);
this.isShowingChildDialog = true;
dialog.show();
},
_onHide: function () {
this.isShowingChildDialog = false;
}
Last updated: Mar 25, 2013