Create an editor widget
Describes how to create a custom dojo widget that will be used by Optimizely's edit view.
Create a simple editor widget
The following example shows the basic structure of a widget class and creates a simple widget for entering a valid email address.
Note
Use the Optimizely eslint-plugin-cms tool to verify that only public non-deprecated Optimizely Content Management System (CMS) API's are used when creating widgets.
define(
[
"dojo/_base/declare",
"dijit/_Widget",
"dijit/_TemplatedMixin"
],
function(
declare,
_Widget,
_TemplatedMixin
) {
return declare([_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>'
})
}
);
Note
The code snippet inherits from the
dijit/_TemplatedMixin
class, which lets you create a template for the user interface of the widget in either a local string or an external HTML file. You can attach to elements in the template, giving you a programmatic reference to that node in the widget, and you also can attach events to event handlers in our widget.
When the widget is created, the initial value is not available. The initial value is set with set('value', value)
 and can be called multiple times when loading the editor user interface. To make sure you update the value in the text box when it is set on the widget, declare a _setValueAttr
method:
_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 _setValueAttr
 method also references a variable named email, which is the textbox DOM node; 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 you must populate the changes made in the widget, call the onChange
method and pass a value to it. You should call onChange
as often as necessary during editing to provide an accurate preview of changes.
_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);
}
The _onChange
method is a private event handler triggered when a change is made on the text box. It is configured in the template using the data-dojo-attach-event
syntax. After it has updated the value, it calls the onChange
method, which in turn causes a page to update.
onChange: function (value)
{
// Event
}
The following example shows how to update the user interface when a user types. In the postCreate
method, connect to the onKeyDown
and onKeyUp
events on the text box element if the intermediateChanges
property is set to true.
postCreate: function ()
{
// summary : Connects keyboard events of the email textbox to update the value of the editor.
// tags : protected
if (this.intermediateChanges)
{
this.connect(this.email, 'onkeydown', this._onIntermediateChange);
this.connect(this.email, 'onkeyup', this._onIntermediateChange);
}
},
_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 where you set the focus when the control loads by implementing the focus
method.
focus: function ()
{
// summary : Put focus on this widget.
// tags : public
dijit.focus(this.email);
}
A complete example of how to create a custom widget (a StringList
) can be found in the Alloy template site available on GitHub.
Check out a topic on how to register a custom editor widget on the server side for how to use the custom editor.
Editor widget properties
There are a few important widget properties worth mentioning:
intermediateChanges
– Indicates whether theonChange
method is used for each value change or only on demand.label
– Title of the property to be edited.value
– Value of the widget.required
– Indicates whether this widget requires a value.
Note
You can add properties for a particular widget by using a property
EditorDescriptor
.
Editor widget methods
onChange
– Callback event that should be raised from within the widget when the value has changed. The wrapper displaying this widget listens to this event and updates the user interface when it occurs.focus
– Called when the widget is displayed, should focus on the start element in the widget.isValid
– Called during validation when an item saves; true if the current value is valid.
Validation
To support validation, the widget must implement the isValid
method.
The constraints for a property are mixed into the widget when it is constructed. For example, if the value has the required checkbox selected in the admin view, then that is passed through as the property required.
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);
}
To add a custom error message when validating, the widget must implement the getErrorMessage
method.
errorMessage: "",
isValid: function () {
// summary : Indicates whether the current value is valid.
// tags : public
if (this.value.length < 10) {
this.errorMessage = "email should have at least 10 characters";
return false;
}
if (this.value.indexOf("optimizely.com") < 0) {
this.errorMessage = "email should come from optimizely.com";
return false;
}
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);
},
getErrorMessage: function () {
return this.errorMessage;
}
Manage child dialog boxes
If your widget needs to launch a dialog box of its own, extend an additional class with epi-cms/widget/_HasChildDialogMixin
, and set a few property values in the correct place to ensure that the blur event (going from the main dialog box to the new one) does not close the main dialog box.
The class provides one additional property called isShowingChildDialog
, used in the blur event of the main dialog to determine whether it should hide itself. If you want to prevent the hiding of the main dialog box when the widget launches a child dialog box, set the value to true before launching the child dialog box, then set it back to false after the child dialog box closes.
_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;
}
Use the custom editor
[ClientEditor(ClientEditingClass = "alloy/component/EmailEditor")]
public virtual string Email { get; set; }
or creating an EditorDescriptor
:
[ClientEditor(ClientEditingClass = "alloy/component/EmailEditor")]
public virtual string Email {
get;
set;
}
Note
Without the
UIHint
, all string properties will be affected and will use the custom EmailEditor widget.
Related topics
Updated 5 months ago