Flex provides many types of built-in formatters and validators that enable you to display and validate user-supplied data such as dates, numbers, and currencies. Using the built-in data validators on the client side, you can make your application perform better by reducing calls to the server for validation. You can also save development time by using the built-in formatters to automate the often repetitive process of formatting data.
Flex formatters convert raw data into a customized string using predefined rules. The formatters can be used in MXML or in ActionScript and work well with data binding to simplify the display of formatted data.
Validators are used to ensure that data meets specific criteria before the application attempts to use it. This can be particularly important if you expect a user to input a number for a mathematical operation or a date for scheduling. Like formatters, validators can be used in MXML or ActionScript. They provide logical feedback on data input (valid or invalid) but also provide visual feedback in the way of red borders and error messages when input is invalid.
A formatter is simply an ActionScript class that descends from (is a subclass of) the Formatter class. Some of the formatters available include:
• mx.formatters.CurrencyFormatter
• mx.formatters.DateFormatter
• mx.formatters.NumberFormatter
• mx.formatters.PhoneFormatter
• mx.formatters.ZipCodeFormatter
Formatters manage quite a bit of complexity for you, but they are exceedingly simple to use. Here is a CurrencyFormatter defined in MXML:
<mx:CurrencyFormatter id="myFormatter"
precision="2"/>
This formatter can be applied either in ActionScript or in MXML with data binding, using the following syntax:
trace( myFormatter.format( 123 ) );
//outputs $123.00 in the United States
<s:Label text="{ myFormatter.format( someData ) }"/>
In the latter example, each time someData
changes, the format()
method will be called and the output displayed in the label.
Previously, you accomplished something similar using concatenation of strings. You wrote code like this:
<s:Label text="Your Cart Total: ${shoppingCart.total}"/>
This strategy has several problems. First, it becomes complicated to control variables defining how the user wants to see this currency presented. For example, if you are creating a globalized application, you will need to support different currency symbols, different regional uses of commas and periods, and varying degrees of precision. Second, this code assumes that the currency symbol will always appear before the number. This is certainly not the case in many countries. By using Flex formatters, these and other issues are handled for you.
Flex also has a set of Validator classes that you can use to check whether a data type is valid (for example, if the input is a number) and to ensure that the input has been formatted correctly (for example, if a date is entered in a specific format). As with Formatter classes, you can use Validator classes either as MXML tags or instantiate them in ActionScript.
Using validators, you can perform a large amount of data validation in the client application, instead of waiting until data is submitted to the server. Not only does this provide a more responsive user experience, it also reduces the number of calls between the client and the server. This yields a better-performing application. Client-side validation is not a perfect solution; some types of data validation (such as security issues) are still best performed at the server. But using Validator classes at the client reduces server calls to only these use cases.
All Flex validators are a subclass of the Validator class. Some of the validators available as part of the Flex framework include:
• CreditCardValidator
• DateValidator
• EmailValidator
• NumberValidator
• PhoneNumberValidator
• SocialSecurityValidator
• StringValidator
• ZipCodeValidator
Much like the Formatter classes, the Validator classes cover a large number of use cases and conditions that you might not consider on your own. By using the Flex validators, you are better prepared for robust applications and internationalization requirements.
In this exercise, you will apply a CurrencyFormatter class so all the price selections are displayed as local currency in the FlexGrocer application. There are multiple places in which prices are displayed in the application, including:
• The list of grocery products displayed
• The total of the shopping cart
• The subtotal and list prices in the user’s shopping cart
• The checkout process
The CurrencyFormatter adjusts the decimal rounding and precision and sets the thousands separator and the negative sign. You can specify the type and placement of the currency symbol used, which can contain multiple characters, including blank spaces.
Alternatively, if you didn’t complete the previous lesson or your code is not functioning properly, you can import the FlexGrocer.fxp project from the Lesson15/start folder. Please refer to Appendix A for complete instructions on importing a project should you ever skip a lesson or if you ever have a code issue you cannot resolve.
<fx:Declarations>
tags, add an <mx:CurrencyFormatter>
tag. Assign the tag an id
of currency
and specify a precision
of 2
:The decimal rounding, thousands separator, currency symbol, and the negative sign are properties that can be set on the CurrencyFormatter; we have left these at their defaults. You specified a precision
of 2
, meaning that two decimal places will always be displayed.
The CurrencyFormatter receives its default settings from user locale information. This locale information exists inside resource bundles that you can create for various locales; however, doing so is beyond the scope of this book.
text
property, replace the dollar sign and binding expression with a call to the format()
method of the currency
object, and pass shoppingCart.total
to the method, as follows:<s:Label text="Your Cart Total: {currency.format(shoppingCart.total)}"/>
The format()
method takes the value and applies all the parameters you set on the <mx:CurrencyFormatter>
tag. In this case, you are using the default currency symbol (a dollar sign for the en_US locale where this book was written) and specifying the precision, so two digits to the right of the decimal separator will always be maintained.
renderProductName()
method.Presently, this method returns a string created by concatenation parentheses around the item quantity along with the product name, a dollar sign, and the item subtotal.
$
from the string and pass the item.subtotal
to the currency.format()
method before using it in the concatenation:Add a few items to the cart and you will quickly see the currency formatter is now adding precision and limiting the number of decimal places to 2. Several other places in the application could use a CurrencyFormatter. You will handle those next.
<fx:Declarations>
tags, add an <mx:CurrencyFormatter>
tag. Assign the tag an id
of currency
and specify a precision
of 2
:renderPriceLabel()
. Change the return
statement of the function to use the format()
method of the currency instance.Previously, you had to cast the subtotal as a String before concatenating with the $
. This isn’t necessary when using formatters. The format()
method accepts an Object and internally converts it as needed.
<fx:Declarations>
tags, add an <mx:CurrencyFormatter>
tag. Assign the tag an id
of currency
and specify a precision
of 2
.<fx:Declarations>
tags, add an <mx:DateFormatter>
tag. Assign the tag an id
of df
:orderInfo.deliveryData
to the format()
method of the df
instance before assigning it to the text.<s:Label text="{df.format(orderInfo.deliveryDate)}"/>
Like the CurrencyFormatter, the DateFormatter has a format method used to convert data into a String. In this case, you are using the default format specified by the user’s locale.
shopppingCart.total
to the format()
method of the currency
instance before concatenating it and assigning it to the text:<s:Label text="Total {currency.format(shoppingCart.total)}"/>
<fx:Declarations>
tags, add an <mx:CurrencyFormatter>
tag. Assign the tag an id
of currency
and specify a precision
of 2
.listPrice
. Pass the product.listPrice
to the format()
method of the currency
instance before assigning it to the text.<s:Label text="{currency.format(product.listPrice)}" id="price"/>
In Lesson 8, “Using Data Binding and Collections,” you learned about data binding, which updates the view components when data changes. Flex 4 introduces a second type of data binding called two-way binding. Two-way binding is most effective when combined with user input forms.
In this type of data binding, view components are updated when data changes; however, the data is also updated when the component changes. You can think of regular data binding as moving in one direction: when the data changes, the view changes. You can think two-way binding as bidirectional: if either changes, the other updates.
In practice, two-way binding is extremely easy to use. There is simply a syntax difference when declaring a control as bound to data.
To bind a TextInput to a piece of data using traditional data binding, your code would look like this:
<s:TextInput text="{someData}"/>
To use two-way binding, you simply prepend the expression with an @
symbol.
<s:TextInput text="@{someData}"/>
Now, if you were to change the someData
variable, the TextInput would update. Additionally, if you were to type into the TextInput, the someData
variable would be updated.
orderInfo
object in the <fx:Script>
block is of type OrderInfo and is marked Bindable.<s:TextInput text="@{orderInfo.billingName}"/>
This means that this field will display the information in the billingName
property of the orderInfo
object. Further, the billingName
property and the entire orderInfo
object will be monitored for changes. If either changes, this field will be updated with the new value. If the user changes the value in this TextInput by typing a new value or deleting what is already there, the billingName
property will be updated to reflect the contents of the field.
Two-way binding does have one limitation at this time: the types of both the source and destination must be the same. With traditional data binding, you can bind a variable declared as a Number to the text input of a Label, even though that Label is expecting a String instance. Traditional data binding will attempt to convert the Number to a String on your behalf. Two-way data binding cannot work unless both the source and destination are the same.
In this exercise, you will use a ZipCodeValidator class to check whether a postal code is a valid U.S. zip code or Canadian postal code along with a StringValidator to ensure that the billing name is at least two characters long during the checkout process.
required
attribute of that FormItem to true
.<mx:FormItem label="Customer Name" required="true">
Setting the required
attribute of a FormItem tag causes Flex to place a red asterisk next to the form field when it is displayed. This is purely a visual property. By itself it does nothing to ensure the user enters data into this field.
id
property of the TextInput to billingName
.<mx:FormItem label="Customer Name" required="true">
<s:TextInput id="billingName" text="@{orderInfo.billingName}"/>
</mx:FormItem>
Shortly, you will need to refer to this TextInput by the id
to validate its input.
required
attribute of that FormItem to true
:<mx:FormItem label="Zip" required="true">
Remember, this is purely a visual detail.
id
property of the TextInput to billingZip
.<mx:FormItem label="Zip" required="true">
<s:TextInput id="billingZip" text="@{orderInfo.billingZip}"/>
</mx:FormItem>
<fx:Declaration>
tag pair, add an <mx:ZipCodeValidator>
tag. Bind the source
property of the ZipCodeValidator to the billingZip
TextInput. Still in the ZipCodeValdator, specify the property
attribute as text
and specify the required
attribute to true
.<mx:ZipCodeValidator source="{billingZip}"
property="text"
required="true"/>
The <mx:ZipCodeValidator>
validates that a string has the correct length for a five-digit zip code, a five-digit + four-digit U.S. zip code, or a Canadian postal code. The source
attribute indicates the control containing the data to be validated. As you will see, this also specifies where any error messages will appear. The property
attribute indicates which property of the control you wish to validate. In this case you are indicating that the text
property of the billingZip
contains the data for validation. Finally, the required
attribute indicates that this field must be supplied. If required
was set to false
, a blank field would be acceptable, but if the user entered any information, it must conform to a valid zip code.
<fx:Declaration>
tag pair, add an <mx:StringValidator>
tag. Bind the source
property of the StringValidator to the billingName
TextInput. Specify the property
attribute as text
, the required
attribute as true
, and minLength
as 2
.<mx:StringValidator source="{billingName}"
property="text"
required="true"
minLength="2"/>
The <mx:StringValidator>
validates that a string falls within certain size parameters. Here you are indicating that the String must be at least a length of 2 to be valid.
Click the Checkout button; enter some letters for the zip code in the billing information screen. When you exit the field, you should see a red highlight around the text field; when you move the pointer over the text field, you will see the default error message appear.
However, even if you leave these fields in error, you can still click the Proceed button to move on to the next screen. You will correct that next.
You will now add code to prevent leaving this page until the user corrects any errors.
<fx:Declarations>
tag pair, wrap the two validators you created above in an <fx:Array>
tag with the id
of validators
.This code creates an array named validators. It inserts the two validator instances created into that array so that they can be referred to as a group.
handleProceed()
method.This method is called when the user clicks the Proceed button. It dispatches an event, which changes to the next view in the ViewStack.
errors
of type Array
on the first line of this method. Assign it to the result of calling the Validator.validateAll()
method, passing it the validators
array you just created.var errors:Array = Validator.validateAll( validators );
If you used code completion, mx.validators.Validator
will be imported for you. If not, import it now. The validateAll()
method is a static method of the Validator class. It is a utility method that accepts an array of validators, like the one you created in step 10. It runs each validator and aggregates any failures, meaning that this array will contain any validation errors found as a result of running each of your validators. If the array is empty, there were no validation errors.
errors
array declaration, create an if-else
statement that checks if the length
property of the errors
array is greater than 0
.if ( errors.length > 0 ) {
} else {
}
Effectively, this if
statement checks if there were any validation errors.
proceed
event inside the else
block.if ( errors.length > 0 ) {
} else {
dispatchEvent( new Event( 'proceed' ) );
}
If there are no errors, the user will be allowed to continue.
show()
method of the Alert class and pass it the string Please fill in all required fields. Here is the final function.private function handleProceed( event:Event ):void {
var errors:Array = Validator.validateAll( validators );
if ( errors.length > 0 ) {
Alert.show( "Please fill in all required fields" );
} else {
dispatchEvent( new Event( 'proceed' ) );
}
}
The Alert class is a handy way to notify the user of an error or problem with the application.
You now have a form capable of collecting valid data, informing the user when that data is invalid, and preventing the user from proceeding if they have not yet corrected the invalid data.
In this lesson, you have:
• Learned how to apply a formatter to incoming text (pages 368–377)
• Learned about two-way data binding (pages 371–372)
• Learned how to apply a validator to outgoing data (pages 372–376)
• Learned to trigger validation from ActionScript (pages 374–375)