loading table of contents...

7.4.2. Standard Component StringPropertyField

The task attempted in this section is to replicate the behavior of the standard StringPropertyField.

Create the new property field as an EXML component, since it is a visual component and needs no application logic. You inherit directly from the Ext JS component TextField that is used for displaying the property. Before you can start, you must set the stage for the XML file.

<?xml version="1.0" encoding="ISO-8859-1"?>
<exml:component xmlns:exml="http://www.jangaroo.net/exml/0.8"
                xmlns="exml:ext.config"
                xmlns:editor=
                "exml:com.coremedia.cms.editor.sdk.config"
                xmlns:ui="exml:com.coremedia.ui.config">

Example 7.24. Custom property field


You need the exml namespace for the basic structure of the EXML file, the default name space for predefined Ext JS components, the editor namespace for CMS-specific components and plugins (the "Editor SDK"), and the ui namespace for generic plugins at the model layer (the "UI Toolkit").

The element <exml:component/> indicates that a component is defined. It does not specify that the new component should inherit from Component directly. If you need a base class, you can specify it using the baseClass attribute - this is shown further down in this example.

Afterwards, the configuration options supported by the class are described, using the <exml:cfg> elements. You can think of the set of these elements as the configuration API description of your component. Any component inherits the configuration options from its superclass(es).

<exml:cfg name="propertyName" type="String">
  <exml:description>
    The property to bind.
  </exml:description>
</exml:cfg>
<exml:cfg name="bindTo" type="com.coremedia.ui.data.ValueExpression">
  <exml:description>
    A value expression evaluating to the content whose
    property is being edited.
  </exml:description>
</exml:cfg>

The two properties propertyName and bindTo are mandatory for all property fields. The former declares the name of the property to be edited, which is used both for accessing the model and for localizing the property field. The latter declares a value expression evaluating to the Content object.

<exml:cfg name="hideIssues" type="Boolean">
  <exml:description>Don't show any validation issues on this property field.</exml:description>
</exml:cfg>

As a third configuration option, you can disable the visual indication of content errors or warnings via configuration. This option will later on passed to the appropriate plugin.

An optional description of the entire class follows.

<exml:description>...</exml:description>

You are now ready to define the base class and add some styling.

<textfield name="{'properties.' + config.propertyName}"
     anchor="100%"
     cls="string-property-field">

Several plugins are available to customize the behavior of the editor.

<plugins>

To register the property field properly with Studio for the purposes of preview-base editing and navigating directly to property field, you need to declare the following plugin:

<editor:propertyFieldPlugin propertyName="{config.propertyName}"/>

Using this plugin lets Studio know that your component is authoring a content property. Among other things, this will set up your component to cooperate properly with the content errors and warnings navigation window, and with content shortcuts from the embedded preview.

By default, a component will flush its state to the server when it loses its focus, which typically happens when a users clicks into another property field. If you want to update the backing model more frequently, you can use the immediate change events plugin. The plugin sends a change event when the user has not typed anything for longer than a configurable "buffer" time (in milliseconds). This plugin works for Field components only, but when you look at the Ext JS class tree, you will find that many components are fields in disguise, even number fields and combo boxes.

<ui:immediateChangeEventsPlugin/>

In order to support content validation, a field should also be highlighted in red (when content errors are present), or orange (when content warnings are present). See Section 7.15.1, “Validators” for information on how to set up server-side content validators. On the client side, the showIssuesPlugin as shown below handles all the work. It reads the issues generated on the server and attaches one of the style classes issue-error and issue-warn if an issue is present. Pass all relevant configuration options from the property field to the plugin, especially the options bindTo and propertyName.

Additionally, this plugin highlights the property field in differencing mode when the property value has changed. To this end, it attaches a style class issue-change to its component if the property is reported as changed by the server.

For struct properties, a dot-separated property path can be used as the property name to visualize issues and differences of a property nested in a struct value.

Because the string property field shown here is based on a plain textfield, all formatting rules are already provided in the standard style sheets. For custom components, it might be necessary to add CSS rules for the style classes issue-error, issue-warn, and issue-change in order to visualize issues and changes correctly.

The propertyFieldPlugin and the showIssuesPlugin are often, but not always attached to the same component. In some cases it may appropriate to designate an outer component as the component to scroll into view when navigating to a property, but to select an inner component to be tagged with issue style classes.

<editor:showIssuesPlugin bindTo="{config.bindTo}"
        propertyName="{config.propertyName}"
        ifUndefined=""
        hideIssues="{config.hideIssues}"/>

The property label is used when displaying the component in a form. Using the following plugin, you can make sure that the label is localized according to the standard localization pattern.

<editor:setPropertyLabelPlugin
        bindTo="{config.bindTo}"
        propertyName="{config.propertyName}"/>

When the string field is empty, you want to display a message instructing the user to enter a text. This, too, can be localized uniformly, and the setPropertyLabelPlugin sets your property field up to play along nicely.

<editor:setPropertyEmptyTextPlugin
        bindTo="{config.bindTo}"
        propertyName="{config.propertyName}"/>

Also, the component should be made read only (meaning that the user cannot enter any text but still can mark and copy the content) when the edited content is checked out by another user or is forced to be read only by the document panel:

<editor:bindReadOnlyPlugin
forceReadOnlyValueExpression="{config.forceReadOnlyValueExpression}"
bindTo="{config.bindTo}"/>

Lastly, the edited value must be passed to the server, and the component should display the server-side value. This ("data binding") is typically done using the versatile bindPropertyPlugin, like shown below. Note that the immediate changes plugin just triggers the change event often enough, whereas the bindPropertyPlugin handles the wiring to the server side, and in turn triggers when a change event is fired.

<ui:bindPropertyPlugin
        bindTo="{config.bindTo.
          extendBy('properties', config.propertyName)}"
        bidirectional="true"/>

Finally, end the component definition.

</plugins></textfield></exml:component>

While the list of plugins may appear quite long at first, it is very helpful to be able to separate the different aspects of a property field in different plugins. If you want to provide a custom algorithm of reacting to an empty value, for example, you can easily do so by just omitting the respective plugin declaration, and providing custom handling code - either in the base class or possibly extracted into your own reusable plugin.