Datagrid item editors and dynamic behavior (part 3/3)
we're ready to wrap up our functionality now.
in part 1, we defined our problem (validated error reporting in a datagrid cell, plus dynamic validation characteristics) and made a first attempt at a solution.
in part 2, we improved our error handling in the cells.
now, in part 3, we'll expand our validation logic beyond valid numbers to range checking based on other XML data in the row. this will fulfill our task of creating item editors with dynamic behavior.
first, we'll change our WeightEditor to define the ranges and accept a 2nd parameter in its validate() method. this parameter specifies the weight class against which we want to validate. its value will come from the row, and our custom ITEM_EDIT_END handler will be able to access it.
here's our new method signature:
in our handler, the old code looked like this:
var userEnteredValue:String = editor.text;
var errorMsg:String = editor.validate(userEnteredValue);
and the new code is this:
var editor:WeightEditor3 = WeightEditor3(weigh_in.itemEditorInstance);
var userEnteredValue:String = editor.text;
var errorMsg:String = editor.validate(userEnteredValue, weightClass);
note that our handler doesn't do anything with the weightClass value except hand it to the validation method. our itemEditor and handler are fairly coupled to each other, but at least we've kept the validation logic in the editor and not spread it across both places.
the handler logic is the same: if we fail our (now improved) validation, we'll prevent the datagrid from moving out of the cell and allow the user to enter another value.
here's the code for the new item editor, WeightEditor3.mxml:
<mx:TextInput restrict="1234567890,\." xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.formatters.NumberFormatter;
import mx.events.ValidationResultEvent;
import mx.validators.NumberValidator;
protected static const A_LOW:Number = 0;
protected static const A_HIGH:Number = 99;
protected static const B_LOW:Number = 100;
protected static const B_HIGH:Number = 135;
protected static const C_LOW:Number = 136;
protected static const C_HIGH:Number = 169;
protected static const D_LOW:Number = 170;
protected static const D_HIGH:Number = 210;
/**
* This method is invoked when the user enters a value in the weight column
* of the weigh_in datagrid. Because we're allowing the user to handle validation,
* we'll always return what the user typed in, right or wrong. By sending back a
* value which will eventually fail validation, that allows us to keep the value
* in the field when the errortip is drawn around it.
*/
override public function get data() : Object
{
var value:String = text;
// no need to validate here, since we don't act upon it. but if we did // need to validate to make some kind of decision, then we'll end up // validating twice.
return value;
}
/**
* This is our validation method. We pass validation if the user has
* entered a non-negative decimal. This is a public function so that we can
* re-use this validation outside of the editor.
*
* @param value The user-entered value
* @param weightClass From the XML, the weight class of this particular wrestler
*
* @return null if we pass validation, otherwise we return the error string
*/
public function validate(value:String, weightClass:String) : String
{
var validator:NumberValidator = new NumberValidator();
validator.allowNegative = false;
validator.required = false;
var result:ValidationResultEvent = validator.validate(value);
if (result.type != ValidationResultEvent.VALID)
return result.message;
var errorMsg:String = validateWeightClass(value, weightClass);
return errorMsg;
}
/**
* This is a validation helper method. It checks to ensure the entered weight
* is within range. The weight can be equal on the low end but must be lower than
* the high end.
*
* @param value The user-entered value
* @param weightClass From the XML, the weight class of this particular wrestler
*
* @return null if we pass validation, otherwise we return the error string
*/
protected function validateWeightClass(value:String, weightClass:String) : String
{
var lowWeight:Number = 0;
var highWeight:Number = 1000;
switch (weightClass)
{
case 'A' :
lowWeight = A_LOW;
highWeight = A_HIGH;
break;
case 'B' :
lowWeight = B_LOW;
highWeight = B_HIGH;
break;
case 'C' :
lowWeight = C_LOW;
highWeight = C_HIGH;
break;
case 'D' :
lowWeight = D_LOW;
highWeight = D_HIGH;
break;
default:
break;
}
var weight:Number = Number(value);
if ((weight >= lowWeight) && (weight < highWeight))
return null;
var errorMsg:String = "This wrestler's weight is out of his weight class. His weight must be between "
+ lowWeight + ' and ' + highWeight + ' pounds.';
return errorMsg;
}
]]>
</mx:Script>
</mx:TextInput>
here's the code for the app (wrestling3.mxml):
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" viewSourceURL="srcview/index.html">
<mx:Script>
<![CDATA[
import mx.events.DataGridEventReason;
import mx.events.DataGridEvent;
import mx.formatters.NumberFormatter;
/**
* Our label function. It formats entered weights as proper numbers.
*
* @param item The user-entered weight
* @param column The column in which the weight was entered
*
* @return A formatted weight
*/
protected function decimalLabel(item:Object, column:DataGridColumn) : String
{
var formatter:NumberFormatter = new NumberFormatter();
var weight:String = item.weight;
var formattedNumber:String = formatter.format(weight);
return formattedNumber;
}
/**
* This is our method for processing ITEM_EDIT_END events on our
* weight column. We'll grab both the weight and weight class and
* validate the weight is within range.
*
* If the weight is within range, we'll let the framework handle the
* rest of the process, such as finding the next cell to edit and calling
* our label function.
*
* If the weight is not within range, we'll set our errorTip to the error
* message we got back from validation, and ensure the framework does not
* continue processing so that the user will stay in the same cell and can
* enter a new value.
*
* @param event Our ITEM_EDIT_END event.
*/
protected function processEditedItem(event:DataGridEvent) : void
{
if (event.reason == DataGridEventReason.CANCELLED)
return;
if (event.dataField != 'weight')
return;
// we need a reference to our style of item editor so we can call // its validation method.
var weightClass:String = event.itemRenderer.data.weightClass;
var editor:WeightEditor3 = WeightEditor3(weigh_in.itemEditorInstance);
var userEnteredValue:String = editor.text;
var errorMsg:String = editor.validate(userEnteredValue, weightClass);
// if we didn't validate, we'll set our errorString and call preventDefault() // so the datagrid knows to keep us in the same cell
if (errorMsg)
{
editor.errorString = errorMsg;
event.preventDefault();
}
}
]]>
</mx:Script>
<mx:XMLList id="wrestlers" xmlns="">
<wrestler>
<name> Bob Smith </name>
<weightClass> A </weightClass>
<weight> </weight>
</wrestler>
<wrestler>
<name> Tim McLarry </name>
<weightClass> B </weightClass>
<weight> </weight>
</wrestler>
<wrestler>
<name> Joe Doug </name>
<weightClass> C </weightClass>
<weight> </weight>
</wrestler>
<wrestler>
<name> Larry McTim </name>
<weightClass> D </weightClass>
<weight> </weight>
</wrestler>
</mx:XMLList>
<mx:DataGrid id="weigh_in" editable="true" dataProvider="{wrestlers}" itemEditEnd="processEditedItem(event)" >
<mx:columns>
<mx:DataGridColumn id="wrestler_name" headerText="Name" editable="false" dataField="name" />
<mx:DataGridColumn id="weight_class" headerText="Class" editable="false" dataField="weightClass" />
<mx:DataGridColumn id="weight" headerText="Weight" editable="true" dataField="weight"
itemEditor="WeightEditor3" editorDataField="data" labelFunction="decimalLabel" />
</mx:columns>
</mx:DataGrid>
<mx:Form >
<mx:FormHeading label="Weight Classes" />
<mx:FormItem label="A" >
<mx:Label text="0-99 lbs." />
</mx:FormItem>
<mx:FormItem label="B" >
<mx:Label text="100-135 lbs." />
</mx:FormItem>
<mx:FormItem label="C" >
<mx:Label text="136-169 lbs." />
</mx:FormItem>
<mx:FormItem label="D" >
<mx:Label text="170-210 lbs." />
</mx:FormItem>
</mx:Form>
</mx:Application>
click on the image to run the final app. test the new validation by entering valid numbers which are outside the weight range.

There are no comments for this entry.
[Add Comment]