In this lesson, you will:
• Use a formatter and remove string concatenation
• Use a validator to check if data is in a valid format
• Learn to trigger validation in ActionScript
Flex provides 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 implements an interface called IFormatter. Some of the formatters available include:
• spark.formatters.NumberFormatter
• spark.formatters.CurrencyFormatter
• spark.formatters.DateTimeFormatter
• mx.formatters.CurrencyFormatter
• mx.formatters.DateFormatter
• mx.formatters.NumberFormatter
• mx.formatters.PhoneFormatter
• mx.formatters.ZipCodeFormatter
You may notice that there are formatters (such as Number and Currency) that exist in both the spark.formatter package and the mx.formatters package. The formatters in the spark.formatters package are newer and have exciting new features such as locale awareness (the ability to format data differently depending on where the user lives/works/uses the application). So, whenever you have the choice, we recommend you use the latest and greatest from the spark package.
Formatters manage quite a bit of complexity for you, but they are exceedingly simple to use. Here is a CurrencyFormatter defined in MXML:
<s:CurrencyFormatter id="myFormatter"
fractionalDigits="2"/>
This formatter can be applied either in ActionScript or in MXML with data binding, using the following syntax:
trace( myFormatter.format( 123.456789 ) );
//outputs USD123.46 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’ll 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 lot 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 implement an interface named IValidator. Some of the validators available as part of the Flex framework include:
• spark.validators.NumberValidator
• spark.validators.CurrencyValidator
• mx.validators.CreditCardValidator
• mx.validators.EmailValidator
• mx.validators.NumberValidator
• mx.validators.PhoneNumberValidator
• mx.validators.SocialSecurityValidator
• mx.validators.StringValidator
• mx.validators.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. They are also split into two packages: spark.validators which contain the latest locale aware validators and the older mx.validators. Use the spark.validators if the particular item you need exists in both packages.
By using the Flex validators, you are better prepared for robust applications and internationalization requirements.
In this exercise, you’ll 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, currency symbol type, and placement as well as sets the thousands separator and the negative sign.
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 the appendix for complete instructions on importing a project should you skip a lesson or if you have a code issue you cannot resolve.
<s:Application>
tag, add a locale attribute and set the value to en_US.
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:views="views.*" xmlns:services="services.*"
xmlns:cart="cart.*"
creationComplete="handleCreationComplete(event)"
locale="en_US">
By adding this attribute, you are specifying the locale of your application. A locale is a combination of a language and country code. So, in this case you are indicating that this particular application will be in English as spoken in the United States. Had you specified en_GB, the application would use English as spoken in Great Britain. Although English is spoken in both countries there are differences in spelling, units of currency, and so on. Later in this lesson, you’ll specify a locale of fr_FR, French as spoken in France to see the differences applied to your application.
<fx:Declarations>
tags, add an <s:CurrencyFormatter>
tag. Assign the tag an id
of currency
and add the useCurrencySymbol
attribute with a value of true
:
<fx:Declarations>
<s:CurrencyFormatter id="currency"
useCurrencySymbol="true"/>
</fx:Declarations>
There is an <mx:CurrencyFormatter/>
and an <s:CurrencyFormatter/>
in this lesson. You are going to need to pay close attention to this difference to ensure the desired outcome.
The fractional digits, grouping separator, currency symbol, and many more properties can be set on the CurrencyFormatter; we have left these at their defaults. The CurrencyFormatter receives its default settings from user locale information. You also specified userCurrencySymbol
of true
, meaning that the formatter should include an appropriate currency symbol.
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 <s: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 the default 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 concatenating parentheses around the item’s quantity along with the product name, a dollar sign, and the item’s subtotal.
$
from the string and pass the item.subtotal
to the currency.format()
method before using it in the concatenation:
private function renderProductName( item:ShoppingCartItem ):String {
var product:Product = item.product;
return '(' + item.quantity + ') ' + product.prodName + ' ' + currency.format(item.subtotal);
}
Add a few items to the cart and you’ll see that 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’ll handle those next.
<fx:Declarations>
tags, add an <s:CurrencyFormatter>
tag. Assign the tag an id
of currency
and add the useCurrencySymbol attribute with a value of true
:
<fx:Declarations>
<s:CurrencyFormatter id="currency"
useCurrencySymbol="true"/>
</fx:Declarations>
renderPriceLabel()
. Change the return
statement of the function to use the format()
method of the currency instance:
private function renderPriceLabel( item:ShoppingCartItem, column:DataGridColumn ):String {
var subtotal:Number = item[ column.dataField ];
return currency.format( subtotal );
}
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 <s:CurrencyFormatter>
tag. Assign the tag an id
of currency
and add the useCurrencySymbol
attribute with a value of true
.<fx:Declarations>
tags, add an <s:DateTimeFormatter>
tag. Assign the tag an id
of df
:
<fx:Declarations>
<s:CurrencyFormatter id="currency"
useCurrencySymbol="true"/>
<s:DateTimeFormatter id="df"/>
</fx:Declarations>
<s:FormHeading label="Review and Checkout"/>
<s:FormItem label="Order Date">
</s:FormItem>
<fx:Script>
block, create a new private function named getDate()
. This method will accept no arguments, and return a Date object.getDate()
method, instantiate and return a new Date
object:
private function getDate():Date {
return new Date();
}
You’ll use this method to add the current date to the review page. You may remember that, in ActionScript, when a new Date object is created, it will reflect the current time.
<s:Label/>
tag, set the text
property equal to df.format( getDate() )
.
<s:FormHeading label="Review and Checkout"/>
<s:FormItem label="Order Date">
<s:Label text="{df.format( getDate() )}"/>
</s:FormItem>
Like the CurrencyFormatter, the DateTimeFormatter 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. You are passing the format()
method the date returned from your getDate()
method. By using the date object and the formatter, you are placing an indicator of the current date and time on the form.
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 <s:CurrencyFormatter>
tag. Assign the tag an id
of currency
and add the useCurrencySymbol
attribute with a value of true
.listPrice
. Pass the product.listPrice
to the format()
method of the currency
instance before assigning it to the text.
<s:Label id="price" text="{ currency.format( product.listPrice ) }"/>
In this exercise, you’ll apply a different locale to the Application and then examine the results.
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:views="views.*" xmlns:services="services.*"
creationComplete="handleCreationComplete(event)"
locale="fr_FR">
Notice that the dollar signs have changed to euro symbols throughout the application. Further, the currency symbol now resides on the right side of the number.
In this exercise, you’ll 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 during the checkout process that the billing name is at least two characters long.
required
attribute of that FormItem to true
.
<s:FormItem label="Customer Name" required="true">
<s:TextInput text="@{orderInfo.billingName}"/>
</s:FormItem>
Setting the required
attribute of a FormItem tag causes Flex to place an 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
:
<s:FormItem label="Customer Name" required="true">
<s:TextInput id="billingName" text="@{orderInfo.billingName}"/>
</s:FormItem>
Shortly, you’ll need to refer to this TextInput by the id
to validate its input.
required
attribute of that FormItem to true
:
<s:FormItem label="Zip" required="true">
Remember, this is purely a visual detail.
id
property of the TextInput to billingZip
:
<s:FormItem label="Zip" required="true">
<s:TextInput id="billingZip" text="@{orderInfo.billingZip}"/>
</s: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’ll 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.
If you used code completion when you added the ZipCodeValidator, the mx
namespace was added for you at the top of this file. If you did not, be sure to add the mx
namespace manually (xmlns:mx=”library://ns.adobe.com/flex/mx”).
<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 and 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’ll 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’ll correct that next.
You’ll 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
.
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
<fx:Array id="validators">
<mx:ZipCodeValidator source="{billingZip}"
property="text"
required="true"/>
<mx:StringValidator source="{billingName}"
property="text"
required="true"
minLength="2"/>
</fx:Array>
</fx:Declarations>
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 state.
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
statement that checks if the length
property of the errors
array is 0
.
if ( errors.length == 0 ) {
}
Effectively, this if
statement checks to see if there were any validation errors.
proceed
event inside the else
block.
if ( errors.length == 0 ) {
dispatchEvent( new Event( "proceed" ) );
}
If there are no errors, the user will be allowed to continue.
You now have a form capable of collecting valid data, informing users when that data is invalid, and preventing users 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 362–368)
• Learned to set a locale for an application (pages 368–369)
• Learned how to apply a validator to outgoing data (pages 369–372)
• Learned to trigger validation from ActionScript (pages 371–372)