Working with Data Entry FormsIN THIS CHAPTERUsing the Form container Creating a Form component Laying out Form controls with the FormItem container Validating data entry Sharing data wit
Trang 1The application in Listing 21.8 displays a line chart and an area chart using the same data.
LISTING 21.8 Line and area charts
<fx:Model id=”trendModel” source=”data/trendData.xml”/>
<s:ArrayCollection id=”trendData” source=”{trendModel.row}”/>
Trang 2Both the LineSeries and AreaSeries components can adjust the shape of their lines based on their form property As displayed in Figure 21.13, the form property has these possible values:
l curve Draws curves between data points
l horizontal Draws vertical lines from the x coordinate of the current point to the x
coordinate of the next point
l reverseStep Draws vertical and then horizontal lines to connect data points
l segment (the default) Draws straight lines to connect data points.
l step Draws horizontal and then vertical lines to connect data points
l vertical Draws vertical lines from the y coordinate of the current point to the y dinate of the next point
coor-Figure 21.14 shows the six different forms of line charts
Trang 3l There are nine types of charts.
l You can determine the visual presentation of a chart by setting its data, properties, and styles
l Pie charts also can be displayed as doughnut charts with hollow centers
l HLOC and candlestick charts are designed to show financial information
l The bar, column, line, and area charts are designed to show comparative or trend data
Trang 5Working with Data Entry Forms
IN THIS CHAPTERUsing the Form container Creating a Form component Laying out Form controls with the FormItem container Validating data entry Sharing data with value objects and custom events
When you start to integrate data into a Flex application, you have
to solve the problem of how to get data into the Flex runtime environment As you have seen in earlier chapters, you can embed data into the application using hard-coded MXML or ActionScript, or
by integrating data into the application with the <fx:Model> tag These strategies, however, only work for data that’s both small and static
For existing data that’s retrieved from a server-based resource, such as a database or an EXtensible Markup Language (XML) file, you can use Remote Procedure Call (RPC) components such as HTTPService, WebService, and RemoteObject
Cross-Reference
For more information about HTTPService, WebService, and
RemoteObject RPC components, see Chapters 23, 25, 26, 28, and 29 nAnd then there’s data that comes from the user Unless an application is used exclusively with static data or content retrieved from a server at runtime, a data-centric application must collect data from the user In this chapter, I describe using the following tools for building data entry form components:
l The Form, FormHeading, and FormItem components for laying out a data entry form
l Validator components to validate a user’s data entry
l Custom value object and event classes to share data with the rest of the application
This chapter also includes tutorials that enable you to integrate many of the techniques described in preceding chapters, including using containers and controls (Chapters 8 and 10), creating custom MXML components (Chapter
Trang 65), modeling data with custom ActionScript classes (Chapter 17), and creating and dispatching custom event objects (Chapter 7).
On the Web
To use the sample code for this chapter, import the chapter22.fxp project from the Web site files into any folder on your disk n
Using the Form Container
The Form component is a MX-based layout container that’s responsible for laying out Form trols and labels in an intuitive, consistent manner
con-Tip
Unlike the HTML <form> element, which collects data and posts it to a server-based resource with an HTTP request, the Flex Form container does not handle application navigation or packaging of data collected from the user Instead, you (the developer) are responsible for declaring data collection objects and sharing them with the application The Form container is never directly responsible for application navigation in Flex; this is handled with the ViewStack and related navigator containers n
<mx:Form backgroundColor=”#EEEEEE” borderStyle=”solid” >
nested components
</mx:Form>
You can nest any visual components within a Form, and they lay out in a single column stacked vertically, just like with the VGroup container But the following components have special behav-iors when nested within a Form container:
l FormHeading This label-style control automatically left-aligns itself in the controls column
l FormItem Use this special container to nest the Form’s controls Controls are stacked in
a single column placed on the right side of the Form.Every Form has two columns Each nested FormItem container has a label property All labels
in the FormItem containers within a single Form are right-aligned with each other by default and stacked in a single column placed on the left side of the form
Trang 7The application in Listing 22.1 declares a Form container with two columns, one on the left for labels and the other on the right for controls The FormItem containers are nested within the
Form and are declared in the order of their vertical presentation The FormHeading control plays its label value left-aligned above the column containing the controls
<mx:FormHeading label=”My Custom Form” fontSize=”14”/>
<mx:FormItem label=”First Name:”>
The code in Listing 22.1 is available in the Web site files as SimpleForm.mxml in the chapter22 project n
Figure 22.1 shows the resulting form, with two TextInput controls and a Button control played in a single column
FIGURE 22.1
A simple data entry form
Trang 8Using the FormHeading control
The FormHeading control is optional; it displays a label that’s aligned with the controls that are wrapped in FormItem containers These default style settings make it display in a larger font than
a default Label control:
l fontSize Sets the label to a default of 12 pixels
l fontWeight Sets the label to a default of bold (compared to normal for other text controls)
You can use as many FormHeading objects as you like For example, in a multi-part form, you might add a FormHeading at the top of each section, as shown in Listing 22.2
<mx:FormHeading label=”Your Personal Information”/>
<mx:FormItem label=”First Name:”>
<s:TextInput id=”stateInput” width=”30”/>
<s:TextInput id=”zipInput” width=”60”/>
Trang 9Figure 22.2 shows the resulting application, with FormHeading controls above each section of the data entry form.
FIGURE 22.2
Using multiple FormHeading controls
FormHeadingcontrols
Some developers prefer not to use the FormHeading, instead wrapping the Form container in a
Panel The Panel container’s title is then used to display a heading, and the FormHeading isn’t necessary The application in Listing 22.3 uses a Spark Panel with a button in its control bar area
Trang 10On the Web
The code in Listing 22.3 is available in the Web site files as FormInPanel.mxml in the chapter22 project n
Figure 22.3 shows the resulting application The Form is wrapped inside a Panel, and the
Button is displayed in the Panel container’s control bar area
FIGURE 22.3
A Form wrapped inside a Panel
Using the FormItem container
The FormItem container is nested within a Form container and in turn contains one or more data entry controls The container’s label property is used to set a string value that is displayed in the
Form container’s left column
Controlling label alignment
By default, the labels in a Form container are right-aligned If you want to change their alignment
to left or center, follow these steps:
1 Create a style selector for the FormItem container.
2 Within the selector, assign the labelStyleName style to an arbitrary style name.
3 Declare the style name selector with text-align set to the new alignment value.
The following <fx:Style> tag set handles each of these tasks:
<fx:Style>
@namespace s “library://ns.adobe.com/flex/spark”;
@namespace mx “library://ns.adobe.com/flex/mx”;
.leftAlignedLabels { text-align: left;
} mx|FormItem {
Trang 11labelStyleName:leftAlignedLabels;
} </fx:Style>
Figure 22.4 shows the visual result The labels within the Form container’s left column are now left-aligned
FIGURE 22.4
A form with left-aligned labels and horizontal layoutLeft-aligned columns
Controlling FormItem layout
Controls within the FormItem container are stacked vertically by default You can change the out rules for any particular FormItem container by setting its direction property to hori-zontal The following code causes the three TextInput controls to lay out side by side, instead
lay-of being stacked on top lay-of each other:
<mx:FormItem label=”City/State/Zip:” direction=”horizontal”>
Setting a default button
In most Web browsers, when the cursor is in an HTML form’s text field and the user presses Enter
or Return, the first “submit” button behaves as though the user has clicked it This is known as
default button behavior and is automatic in those browsers that support it.
Trang 12The Flex Form container does not have an automatic default button, but you can create the ior by setting the Form container’s defaultButton property This property is designed to refer
behav-to a Button object somewhere in the current application or component; you set it with a binding expression that refers to the target Button object by its id
Setting a default button in Flex causes these behaviors:
l When any control in the Form container has focus, the default button shows a colored glow, indicating that pressing Enter or Return is the same as clicking the button
l When the user presses Enter or Return, the Button object’s click event is dispatched
The application in Listing 22.4 has a simple Form container with its defaultButton property set to a Button control with an id of saveButton The user can click the button or press Enter
or Return with the cursor in a TextInput control; either way, the Button object’s click event
border-style:solid;
border-color:#000000;
} </fx:Style>
<mx:Form defaultButton=”{saveButton}”
horizontalCenter=”0” verticalCenter=”0”>
<mx:FormHeading label=”Your Personal Information”/>
<mx:FormItem label=”First Name:”>
<s:TextInput id=”firstNameInput”/>
</mx:FormItem>
<mx:FormItem label=”Last Name:”>
Trang 13ref-Panel (outside the Form container) n
Using Custom Form Components
Data entry forms can be designed as fully encapsulated components that handle all the normal tasks of data entry:
l Presentation of a data entry interface
l Collection and validation of data entered by the user
l Sharing of data with the rest of the application with custom value object and event classes
In this section, I describe the steps to create and use a custom Form component
Creating a custom Form component
You can create a custom Form component as an MXML component with <mx:Form/> as its root element Flash Builder does a particularly nice job of helping you lay out Form components in Design view Try these steps to create a simple Form component:
1 Open the chapter22 project from the Web site files Notice that the project’s source
root folder has a package named forms
2 Right-click (Ctrl+click on the Mac) the forms package.
3 Select New ➪ MXML Component from the context menu.
Trang 144 In the New MXML Component wizard, set these properties (shown in Figure 22.5):
l Filename: LoginForm.mxml
l Based on: mx.containers.Form
l Width: [blank value]
l Height: [blank value]
FIGURE 22.5
Creating a Form component with the New MXML Component wizard
5 Click Finish to create the new component The new component should appear in
Flash Builder
6 If the component opens in Design mode, click Source to switch to Source mode.
The beginning code for the Form component looks like this:
Trang 15Adding controls to a Form component
When you are building a Form component, Flash Builder’s Design mode lets you easily drag and drop the objects you want to use from the Components view Each time you add a control to a
Form container, Design view automatically wraps the control in a new FormItem container You can then set the FormItem container’s label property, drag its handles to resize it, set other properties and styles in the Flex Properties view, and otherwise customize the control’s appearance and behavior
Follow these steps to add data entry form controls to the LoginForm component that was described in the preceding section:
1 Open the LoginForm.mxml file in Design mode.
2 Locate the TextInput control in the Text section of the Components view.
3 Drag the control into the editor region, and drop it inside the Form area As shown
in Figure 22.6, you should see that the TextInput control is wrapped in a FormItem
container automatically, with a default label property of Label
FIGURE 22.6
A TextInput control wrapped in a FormItem container in Design mode
4 Double-click the FormItem container’s label region When the label turns into an
input control, type the label Email Address:.
5 Click the new TextInput control in Design view.
6 In the Properties view, change the TextInput control’s id to emailInput.
7 Drag another TextInput control from the Components view into the form.
Caution
To ensure that a new FormItem container is wrapped around the new control, make sure the blue insertion line that’s displayed during the drag-and-drop operation is as wide as the existing FormItem container (shown
in Figure 22.7) If it’s the size of the TextInput control when you release the mouse button, the new
TextInput control will be dropped into the existing FormItem container n
8 Double-click the label of the new FormItem container and change it to Password:.
9 Change the new TextInput control’s id to passwordInput.
Trang 16FIGURE 22.7
The insertion indicator is the width of the existing FormItem, meaning that you’ll ate a new FormItem when you drop the component
cre-The blue insertion indicator
10 Drag a Button control into the Form and place it below the existing FormItem
container, in its own container.
11 Double-click the new Button control and change its label to Log In.
12 Double-click the label of the new FormItem container and delete the default value
The component should now appear as it does in Figure 22.8
FIGURE 22.8
The Form component in its current state
13 Switch to Source mode
The Form component’s source code should now look like this:
Trang 17When creating a Form component in Design mode, it’s easy to accidentally change the id property of the
FormItem container instead of its nested component The purpose of the id is to enable you to easily collect data from the Form controls when the user clicks the button or otherwise indicates that data entry is complete
You care about the data in the controls, not any data that might be associated with the FormItem containers n
Validating Data Entry
When a user enters data into any database application, you typically want to ensure that it matches specific criteria before sending it to the server or saving it into a persistent data store Flex provides
a set of ActionScript classes in the mx.validators package that are designed for this purpose
Each of the following classes validates a particular data type:
l CreditCardValidator Checks that a String has the correct length and prefix and passes the Luhn mod10 algorithm for the specified card type
l EmailValidator Checks that a String follows the common pattern of an e-mail address
l NumberValidator Checks that a value is a Number or a String that can be parsed as
a number; can be customized for particular numeric ranges and other rules
l PhoneNumberValidator Checks that a value matches a valid phone number pattern;
can be customized for particular locales and other specific rules
l RegExpValidator Checks that a String matches a regular expression
l SocialSecurityValidator Checks that a String matches a valid Social Security number pattern
l StringValidator Checks for String values that match your specific criteria, ing minimum and maximum length
includ-l ZipCodeValidator Checks that a String matches a valid ZIP code pattern
Trang 18All validator classes are extended from mx.validators.Validator, so they’re all used the same basic way.
Creating a validator object
You can create validator objects with either MXML or ActionScript Each validator object is assigned to a single control and implements these required properties that determine its behavior
at runtime:
l property The name of the source object’s property that contains the value to be validated
l source A reference to the data entry control being validated
For example, assume your data entry form includes this TextInput control, which you want to validate as an e-mail address:
<s:TextInput id=”emailInput”/>
The validator object declaration for this control would minimally include the source, referencing the TextInput control’s id in a binding expression, and the property, referencing the input con-trol’s text property as a string:
Controlling validation with trigger events
By default, validation occurs either when the user completes a change to a control’s value or when
he simply clicks or tabs into the control to give it focus and then clicks or tabs again to remove focus This automatic validation is controlled by two properties that are shared by all validator classes:
Trang 19l trigger A reference that points to an object that will trigger validation.
l triggerEvent A String containing the name of the event that will trigger validation
Validation happens automatically when a control loses focus because the validator object’s trigger property defaults to the value of its source property (the control being validated) and triggerEvent defaults to the valueCommitted event Normally, this event occurs when a change is made or the control simply loses focus
You can change when validation occurs by changing these properties’ values For example, in an application where you want all controls to be validated when the user clicks a button, you would follow these steps:
1 Add a unique id to the Button control you want to use as the trigger.
2 Set each validator object’s trigger property to the Button control’s id in a binding
expression.
3 Set each validator object’s triggerEvent property to the event name click.
Follow these steps to add automatic validation to the LoginForm component you created in vious sections of this chapter:
1 Open LoginForm.mxml in Source view.
2 Locate the Button control with a label of Log In, and add an id of loginButton.
3 After the <mx:Form> start tag, declare an <fx:Declarations> tag set.
4 Within the <fx:Declarations> tag set, declare an EmailValidator object with
MXML code Set its id to emailValidator, source to the emailInput control,
property to text, trigger to loginButton, and triggerEvent to click:
5 Add a StringValidator object inside the <fx:Declarations> element with an
id of passwordValidator Set its source to passwordInput and all other erties exactly like the first validator object:
Trang 20The code in Listing 22.5 is available in the Web site files as LoginFormAutoValidation.mxml in the
forms folder of the chapter22 project n
To see the effect of this form, follow these steps to create a new application and incorporate the
Form component:
1 Create a new MXML application named ValidationDemo.mxml.
2 Add an instance of the new LoginForm component Set its id property to
login-Form
Note
As you type, Flash Builder should add the required custom forms namespace prefix for the forms folder to the <s:Application> tag n
3 Set the LoginForm object’s horizontalCenter property to 0 and top to 20 The
application code should appear as follows:
<?xml version=”1.0” encoding=”utf-8”?>
<s:Application xmlns:fx=”http://ns.adobe.com/mxml/2009”
Trang 214 Run the application in a browser
5 Click the LoginForm’s Log In button to trigger validation.
At runtime, as the user clicks the button to trigger validation, each of the validator objects ines the named property of its source data entry control If validation rules pass, the user sees no feedback If a validation rule is broken, the source control displays a red border to the user When the user moves the cursor over the control, she sees a pop-up window displaying the error mes-sage, as shown in Figure 22.9
FIGURE 22.9
A form displaying a validation error message
Controlling validation with ActionScript
Trigger-based validation lets users know that they have entered invalid values but doesn’t give you (the developer) an opportunity to handle the situation and decide whether to continue with form processing or cancel processing and display an error In the previous examples, if you were to exe-cute a function on the button’s click event, that function would execute regardless of whether the validation passed
For most Form components, triggering validation with ActionScript code enables you to find out immediately whether all your validations have passed and to take appropriate action
Disabling validation trigger events
When you use programmatic validation, you typically disable the automatic validation that results from using the trigger and triggerEvent properties You accomplish this by removing the validator object’s trigger property and setting triggerEvent to a blank String:
<mx:EmailValidator id=”emailValidator”
source=”{emailInput}” property=”text”
triggerEvent=””/>
Trang 22Because no event can be dispatched that would have a blank string for its event name, this results
in disabling any event-based validation This is necessary as deleting both properties would cause default validation to occur upon the valueCommitted event of the source object
Triggering individual validator objects with ActionScript
To programmatically trigger validation on a single validator object, call the object’s validate()
method This method returns an instance of the ValidationResultEvent event class:
var validObj:ValidationResultEvent = emailValidator.validate();
As with all event classes, ValidationResultEvent has a type property You determine whether validation has succeeded by comparing the event object’s type to the event name con-stants VALID and INVALID For example, this conditional ActionScript block would execute only
if validation is passed:
if (validObj.type == ValidationResultEvent.VALID){
process data
}
The version of the custom Form component in Listing 22.6 triggers validation programmatically
on two separate validator objects and then evaluates both resulting event objects to determine whether the Form’s data is valid
var emailObj:ValidationResultEvent = emailValidator.validate();
var pwordObj:ValidationResultEvent = passwordValidator.validate();
return (emailObj.type == ValidationResultEvent.VALID &&
pwordObj.type == ValidationResultEvent.VALID);
} protected function loginButton_clickHandler(event:MouseEvent):void {
if (isValid()) Alert.show(“Data is valid”, “Validation Logic”);
else
Trang 23Alert.show(“There are form errors”, “Validation Logic”);
} ]]>
The code in Listing 22.6 is available in the Web site files as LoginFormSingleValidation.mxml in the
forms folder of the chapter22 project n
Triggering multiple validator objects with ActionScript
As a data entry form becomes more complex, with additional controls and validators, calling the
validate() method on each validator object can be cumbersome An alternative approach is to use the Validator class’s static validateAll() method to trigger multiple validator objects simultaneously
To use this approach, call validateAll()and pass in an Array of validator objects:
var arInvalid:Array = Validator.validateAll(
[emailValidator, passwordValidator]);
The validateAll() method returns an Array containing ValidationResultEvent objects only for those validator objects that fail validation If the Array has no items, this means that all validators passed their validation rules The following code evaluates the returned Array:
if (arInvalid.length == 0){
Alert.show(“Data is valid”, “Validation Logic”);
return true;
}
Trang 24Try these steps to add programmatic validation of multiple validator objects to the LoginForm
component described in previous sections:
1 Open LoginForm.mxml in Source view.
2 For both of the existing validator objects, remove their trigger property and set
their triggerEvent to a blank string:
<mx:EmailValidator id=”emailValidator”
source=”{emailInput}” property=”text” triggerEvent=””/>
<mx:StringValidator id=”passwordValidator”
source=”{passwordInput}” property=”text” triggerEvent=””/>
3 Add an <fx:Script> tag set just below the </fx:Declarations> end tag.
4 Create a new private function named isValid() that accepts no arguments and
returns void.
5 Within the function body, use the Validator.validateAll() method to trigger
both the emailValidator and the passwordValidator objects:
var arInvalid:Array = Validator.validateAll(
[emailValidator, passwordValidator]);
As you type the code, Flash Builder might automatically add an import statement for the
Validator class If this doesn’t happen, add this import statement above the
isValid() function:
import mx.validators.Validator;
6 Add the following code after the call to validateAll() to evaluate whether
vali-dation rules were passed:
if (arInvalid.length == 0){
Alert.show(“Data is valid”, “Validation Logic”);
return true;
}else{ Alert.show(“There are form errors”, “Validation Logic”);
return false;
}
7 Locate the Button control with the Log In label, and add a click event handler
that calls the isValid() method:
<s:Button id=”loginButton” label=”Log In”
click=”isValid()”/>
8 Save the Form component file, and open ValidatorDemo.mxml, the application
that was created in a preceding exercise.
9 Run the application, and try clicking the form’s button to trigger validation.
As shown in Figure 22.10, you should see that validation is triggered and a pop-up window duced by the Alert class is displayed After you click OK to clear the pop-up window, a valida-tion error message is displayed when you move the cursor over any control with a red border
Trang 25The target of the ValidationResultEvent objects in the Array refers back to the validator object that failed You can then refer to the validator object’s source property to get a reference to the control that was validated, or the message property to access the validation error message as a String n
FIGURE 22.10
Results of validation with ActionScript
Controlling validation rules and error messages
Each validator class has a set of validation rules and equivalent error messages that are displayed when the rules are broken One of these rules, named required, is implemented on the
Validator superclass and, therefore, is used for all validator objects
The required rule is a Boolean value that defaults to true As a result, when you apply a dator object to a Form control, you’re automatically indicating that the control’s value can’t be left blank When this rule is broken, the value of the validator object’s requiredFieldError prop-erty is displayed in the pop-up error message The default error message for the required-FieldError (in the American English locale) is “This field is required.” You can customize the error message by setting the appropriate property:
vali-<mx:EmailValidator id=”emailValidator”
source=”{emailInput}” property=”text” triggerEvent=””
requiredFieldError=”Email address can’t be left blank”/>
<mx:StringValidator id=”passwordValidator”
source=”{passwordInput}” property=”text” triggerEvent=””
requiredFieldError=”Password can’t be left blank”/>
Each of the two validator objects now has its own distinct error message As shown in Figure 22.11, the user gets better, more specific feedback when she makes a data entry error
Table 22.1 describes some commonly used validation rules and equivalent error message ties This is not an exhaustive list; see the product documentation for a complete list of validation rules and their equivalent error message property names