itemEditors – Part 1

inline itemEditors

I recently completed a series on itemRenderers – customizations to list controls which format the display of the list contents. Displaying and rendering content is very cool and with Flex you can do nearly anything you can imagine.

This new series covers itemEditors – a way to allow data to be changed directly inside of a list control. This first article covers inline itemEditors which are very simple, though quite useful, components you write directly inside your MXML files. The other articles in the series will cover more complex editing, validation, events, and using itemRenderers as itemEditors.

The source code to this article is available by downloading it here.

TextInput Editor

It is nice to edit directly in the list controls. You can imagine a DataGrid of warehouse inventory where you can adjust the content right in the grid without needing a special pop-up. The list controls have a built in editor – a TextInput control – which appears whenever the user clicks the mouse in an editable area, either a row (for a List), a branch (for a Tree), or a cell (for a DataGrid). All you need to do is set the list control’s editable property to true. For a DataGrid you can exclude a column from being edited by setting the DataGridColumn’s editable property to false.

Before editing a cell
After clicking on a cell, the editor opens and the content is ready for editing.

itemEditors differ from itemRenderers in that only one instance of the itemEditor is seen – just on the cell being edited. The itemEditor is not seen until the cell to be edited receives input focus. Then the itemRenderer is hidden and the itemEditor is moved to that position, sized to fit the area, and given the data for the record. When editing is finished (by moving focus to another location), the list control copies the new value from the editor to the dataProvider record.

Using the image above as an example, when the user clicks in a cell of the "Part #" column, the dataProvider[row][dataField] value is given to the text property of the itemEditor (TextInput) control. When editing is finished, the text property value from the itemEditor (TextInput) control is copied to the dataProvider[row][dataField]. The dataProvider, being a collection, dispatches an event in response to the update.

While the default TextInput control makes a fine editor, it really only works for the most simple of cases. It works fine for String values, for example, such as a book title, author name, or product number. If you need more control or want to validate the user’s input, then you need to take matter into your own hands.

Flex Controls as itemEditors

Here is how you make an itemEditor which only accepts numeric values:

	<mx:DataGrid x="46" y="270" editable="true" dataProvider="{employeeDB}">
<mx:columns>
<mx:DataGridColumn headerText="Name" dataField="name"/>
<mx:DataGridColumn headerText="Position" dataField="position"/>
<mx:DataGridColumn headerText="Age" dataField="age">
	<mx:itemEditor>
<mx:Component>
<mx:TextInput restrict="0-9" maxChars="3" />
</mx:Component>
</mx:itemEditor> 
</mx:DataGridColumn>
</mx:columns>
</mx:DataGrid>

A very common control to use for an itemEditor is the CheckBox. This is very useful for editing Boolean values. Here is an example of using the CheckBox to edit the values for an "In Stock" column of an inventory program:

<mx:DataGrid x="531" y="273" editable="true" dataProvider="{inventoryDB}">
<mx:columns>
<mx:DataGridColumn headerText="Product" dataField="product"/>
<mx:DataGridColumn headerText="Part #" dataField="part"/>
	<mx:DataGridColumn headerText="In Stock?" dataField="inStock"
labelFunction="inStockLabeler"
itemEditor="mx.controls.CheckBox" editorDataField="selected" /> 
<mx:DataGridColumn headerText="Quantity" dataField="quantity"/>
</mx:columns>
</mx:DataGrid>

In this example the content of the cells in this column are rendered using a labelFunction (inStockLabeler) which could display anything such as "Yes", "No", "In Stock", or "Out of Stock". The itemEditor property is set to the mx.controls.CheckBox class. And there is another, equally important, property set on the DataGridColumn: editorDataField. This field indicates the property of the itemEditor class to use to fetch the value when editing is finished. In this case it is the CheckBox’s selected property. When editing is finished, the DataGrid will use the CheckBox’s selected property to replace the inStock property in the data record.

You may wonder why the example with the TextInput did not supply the editorDataField property. That is because the default value for editorDataField is "text" which just happens to be name of the property on the TextInput control holding the value.

You can use this same technique with a number of Flex controls. Here is one for an order quantity column using NumericStepper:

	<mx:DataGrid x="531" y="82" editable="true" dataProvider="{inventoryDB}">
<mx:columns>
<mx:DataGridColumn headerText="Product" dataField="product"/>
<mx:DataGridColumn headerText="Part #" dataField="part"/>
<mx:DataGridColumn headerText="In Stock?" dataField="inStock"/>
	<mx:DataGridColumn headerText="Quantity" dataField="quantity"
itemEditor="mx.controls.NumericStepper" editorDataField="value"/> 
</mx:columns>
</mx:DataGrid>

Notice the editorDataField is "value" – the property of the NumericStepper which holds the current value of the control. Make sure you use the fully-qualified class name for the itemEditor property.

Complex Editor

Now suppose you want to do something a little more complex that doesn’t have a ready-made Flex control available. Here is one which allows a credit card number to be entered using 4 separate 4-digit fields:

	<mx:DataGrid x="46" y="463" editable="true" dataProvider="{accountDB}" width="302">
<mx:columns>
<mx:DataGridColumn headerText="Account" dataField="account" width="100"/>
<mx:DataGridColumn headerText="Credit Card" dataField="ccard" editorDataField="value">
<mx:itemEditor>
<mx:Component>
<mx:HBox>
<mx:Script>
<![CDATA[
public function get value() : String
{
return part1.text+part2.text+part3.text+part4.text;
}
override public function set data(value:Object):void
{
super.data = value;
part1.text = value.ccard.substr(0,4);
part2.text = value.ccard.substr(4,4);
part3.text = value.ccard.substr(8,4);
part4.text = value.ccard.substr(12,4);
}
]]>
</mx:Script>
<mx:TextInput id="part1" maxChars="4" restrict="[0-9]" width="40" />
<mx:TextInput id="part2" maxChars="4" restrict="[0-9]" width="40" />
<mx:TextInput id="part3" maxChars="4" restrict="[0-9]" width="40" />
<mx:TextInput id="part4" maxChars="4" restrict="[0-9]" width="40" />
</mx:HBox>
</mx:Component>
</mx:itemEditor>
</mx:DataGridColumn>
</mx:columns>
</mx:DataGrid>

This inline itemEditor follows the same rules as other itemEditors and names the editorDataField as "value". The component chosen for the itemEditor is the HBox – which does not have a "value" property. To make this itemEditor work, a getter function named value is created to return the concatenation of the 4 input fields. Now when editing for the cell completes, the DataGrid can call upon the value property of the itemEditor and it will receive the combined fields.

You can also see that I have overridden the data setter function. In that function I split up the credit card number among the four TextInput fields. This is the technique you use to display the data to be edited. The editorDataField is the property used to retrieve the new value.

Conclusion

In this article you’ve seen how to create an inline itemEditor – from simply naming a class to creating a complex class right within the MXML tags. By naming the property of the editor class which contains the final editor value, the DataGrid can retreive the value from the editor instance and replace the current value in the data.

The next article covers more complex itemEditors and editing events.

11 Responses to itemEditors – Part 1

  1. Flex user says:

    Another great article, thanks. I’m looking forward to your article on itemRenderers as editors.

  2. Excellent article. Really helpful.
    Just a comment regarding the last case scenario, where a complex component serves as item editor. You used the technique of getter and setter, there is another possibility, I believe, and that is to define a public property in the enclosing container, and map that to the editorDataField. Inside the editor control, you use the updateComplete event to set the property.
    This technique is covered here :
    http://livedocs.adobe.com/flex/3/html/celleditor_4.html

    I have no idea what is preferrable, not even if they are interchangeable in all scenarios. Any thoughts?

    Daniel Szmulewicz

  3. Craig Hollabaugh says:

    Peter,
    In Complex Editor, why ‘super.data = value’ in the HBox data setter?
    Thanks,
    Craig

  4. Peter Ent says:

    AH – good question. It is VITAL that you do super.data = value in the data setter function. The data property – really the data getter function – is used extensively behind the scenes in the List controls and elsewhere in the framework (not too mention your own code). If you don’t see the internal value of data using super.data, then the data getter function will return a null value and, most likely, cause your app to crash.

  5. Craig Hollabaugh says:

    Looks like I didn’t read the Complex Editor carefully. I thought the HBox had data getter function, but no. Now I understand why the super.data = value line exists. Thanks for the quick reply.

  6. Leo T says:

    Hi Peter,

    Thanks for the wonderful articles. Your website has been a great asset in my learning of Flex.

    I’m running into a problem with my custom itemeditor.

    I have a datagrid that displays an arraycollection of Flight objects.

    My Flight objects includes flight name, departure time, arrival time, etc.

    I created a custom “time-picker” itemeditor that accepts a Date object as it’s data.

    However, even though I specified the dataField to departure [which is a date object] in my datagridcolumn , I find that my itemeditor is receiving a Flight object instead.

    Of course, I can just hard code the field into my “time-picker” component by specifying within the setter method that “super.data = value.departure as Date”, but then I lose all re-usability of my “time-picker” component.

    Doing so also means that I must now create two different “time-picker” itemeditor components, one for my departure time and one for my arrival time.

    Is there a better way of doing this? How can I specify in the datagrid to pass a specific field from the dataProvider instead of having it pass the whole Flight object to my itemeditors?

    Thanks a million in advance!

    Leo T

  7. Peter Ent says:

    Leo,

    The item renderer is supposed to receive the entire item for the row it is displaying. This allows itemRenderers (and editors) to combine fields (eg, a first and last name into a single display) or use one field to determine the display of another field.

    You can use the listData assigned to the itemEditor to determine what column is being used for an itemEditor instance. Check out the itemRenderer series for an explanation.

  8. k.krishnaprasad says:

    Hi ,

    In our application we are using a data gird. Our requrement is to add button and combo in the same cell of the datagrid.Every thing is working fine except that when we are tabing through the cells .The cell with combo and Botton is not getting the focus if we are clicking only we are able to open and use the combo or the button. Please let me know how to overcome this..

    Thanks
    krishna

  9. Peter Ent says:

    Your editor must implement the IFocusManager interface.

  10. Keith says:

    Hey Peter,

    Great article! I have a question regarding the example with using CheckBox itemEditors:

    Is it possible to extend the example to work with custom item renderers that contain a CheckBox?

    Instead of using ‘itemEditor=”mx.controls.CheckBox”,’ is it possible to have a custom ItemRenderer (in my case a label + checkbox), and use the ItemRenderer’s CheckBox’s “selected” parameter?

    The problem is that I don’t know how to refer to the nested checkbox’s “selected” property. Any suggestions?

    Any help would be greatly appreciated.

    Thanks!
    Keith

  11. Peter Ent says:

    Follow the example of making a more complex itemEditor, shown at the end of the article. You’ll need to make your own “selected” property and use it to set and get the CheckBox within your editor.