Using SmartForm

Flex and Forms

In a rich internet application, data manipulation by the user represents a real challenge. Despite the important number of graphical controls provided by the Flex framework, the developer still resort to forms. The increase in the number of views, data types to handle implies for the developer the creation of an important number of forms, containing various data with various depths and surfaces. Flex does not really provides a way to manage efficiently the forms, especially concerning the insertion and retrieval of value objects, which have to be done manually by the developer.

If you follow the guidelines of the Flex documentation about forms, the use of mx:Model is encouraged. Every member of the value object will have to be manually bound to a form control (TextInput, ComboBox, CheckBox…). This operation is, at the same time, costly and error-prone. One of the other problems while using this technique is that Flex mainly handles complex objects using references: the modifications made by the user on the fields pass directly to the initial data. If the developer wants to cancel the user modifications, he will need to have made manually a copy of the object to be able to restore it.

The SmartForm feature has been created to ease the forms management while developing Flex applications. It provides solutions to create, modify (or simply display) data using forms, with a different approach that the one provided built-in by Flex.

Why should I use SmartForm ?

  1. The SmartForm purpose is to ease input/output of data when working with forms. Using SmartForm, the “id” property of each form control is mapped to the properties of the data object.
  2. SmartForm works on objects by copy instead of reference, allowing you, for example, to revert modifications made by the user.
  3. The Foundry provides tools to be able to work with strong-typed objects, even when editing objects with forms.
  4. SmartForm allows you to switch easily between edit and a read-only modes.

Requirements

The use of the ActionScript Foundry assumes the IBasicObject interface as the basic type for value objects. SmartForm has been created to work with IBasicObject instances. Learn more about IBasicObject here .

SmartForm in Action

SmartForm Elements

The Toolbox package provides the ISmartFormElement interface, used to design form elements that can work in conjunction of SmartForm. Each of the standard controls of the Flex framework has been extended to make the creation of smart forms easier:

  • SFTextInput extends TextInput implements ISmartFormElement
  • SFComboBox extends ComboBox implements ISmartFormElement

How it works

The SmartForm is really simple to setup, and will map automatically the object properties to the form elements. The only requirement is that form elements ids must match properties of the value object. If the CustomerVO class has a firstName property that we need to display or modify, the corresponding form element should have its “id” set to “fisrtName”. When using complex objects containing sub-objects, the “$” sign is used to go through the path (e.g. “address1$streetAddress”). There is no limit on the “depth” of the value objects to map to the form.

Basic steps

The following code describes the CustomerVO type which we want to manipulate using our form instance.

package org.servebox.sample.value
{
	import org.servebox.foundry.value.AbstractValueObject;
 
	public class CustomerVO extends AbstractValueObject implements ICustomerVO
	{
		private var _firstName : String;
		private var _lastName : String;
		private var _email : String;
		private var _city : ICityVO;
 
		/**
		 * The customer's firstName
		 */
		public function get firstName() : String
		{
			 return _firstName;
		}
		public function set firstName<span style="padding: 0pt; display: inline; font-size: inherit; color: black;">( </span>value : String ) : void
		{
			_firstName = value;
		}
 
		/**
		 * The customer's lastName
		 */
		public function get lastName() : String
		{
			 return _lastName;
		}
		public function set lastName<span style="padding: 0pt; display: inline; font-size: inherit; color: black;">( </span>value : String ) : void
		{
			_lastName = value;
		}
 
		/**
		 * The customer's email address
		 */
		public function get email() : String
		{
			 return _email;
		}
		public function set email<span style="padding: 0pt; display: inline; font-size: inherit; color: black;">( </span>value : String ) : void
		{
			_email = value;
		}
 
		/**
		 * The customer's city, please notice this property is a complex object.
		 */
		public function get city() : ICityVO
		{
			 return _city;
		}
		public function set city<span style="padding: 0pt; display: inline; font-size: inherit; color: black;">( </span>value : ICityVO ) : void
		{
			_city = value;
		}
 
	}

To be able to create or update customer objects, we now create the following SmartForm, exactly like we may have done using the standard Form class:

	<mx:Script>
		<![CDATA[
			import org.servebox.sample.value.CityVO;
 
                        [Bindable]
                        // This accessor is used as the dataProvider for the City combobox
			public function get cities() : Array
			{
				var city1 : CityVO = new CityVO();
				city1.zipCode = "75000";
				city1.city = "Paris";
 
				var city2 : CityVO = new CityVO();
				city2.zipCode = "EC1-4";
				city2.city = "London";
 
				var city3 : CityVO = new CityVO();
				city3.zipCode = "1000X";
				city3.city = "New York";
 
				return [city1,city2,city3];
			}
		]]>
	</mx:Script>
 
	<mx:VBox width="100%" height="100%" horizontalAlign="center" verticalAlign="middle">
 
		<form:SmartForm
			id="customerSmartForm"
			styleName="form">
 
			<mx:HBox horizontalAlign="center" verticalAlign="middle">
				<mx:Label text="Manage User" fontWeight="bold"/>
			</mx:HBox>
 
			<mx:FormItem label="First name">
				<elements:SFTextInput id="firstName" />
			</mx:FormItem>
 
			<mx:FormItem label="Last name">
				<elements:SFTextInput id="lastName" />
			</mx:FormItem>
 
			<mx:FormItem label="Email">
				<elements:SFTextInput id="email" />
			</mx:FormItem>
 
			<mx:FormItem label="City">
				<elements:SFComboBox id="city" dataProvider="{cities}" labelField="city"/>
			</mx:FormItem>
 
			<mx:HBox horizontalAlign="center" verticalAlign="middle" width="100%" paddingTop="10">
				<mx:Button label="reset" />
				<mx:Button label="save" />
			</mx:HBox>
 
		</form:SmartForm>
 
	</mx:VBox>

Now our SmartForm is ready to be used.

Object mapping

The object mapping has already been done when we set the id of our form elements. The only thing we have to do now is to give a “source” to our SmartForm instance. You may use any DictionaryTable or IBasicObject instance as the source property of a SmartForm. When using a DictionaryTable instance, the form elements ids are mapped to the DictionaryTable keys. When using an IBasicObject, such as our CustomerVO, the SmartForm will use the object properties to create a DictionaryTable instance, as shown in the table below.

Content of the source DictionaryTable generated from a CustomerVO :

key value
firstName John
lastName Doe
email jdoe@servebox.org
city$city New York
city$description The Big Apple
city$zipCode 1000X
city$photo newyork.jpg

If the property keepObjectInSource of the SmartForm instance is set to true, the sub-objects will be included in the table, not just their properties.

key value
firstName John
lastName Doe
email jdoe@servebox.org
city CityVO instance
city$city New York
city$description The Big Apple
city$zipCode 1000X
city$photo newyork.jpg

We now create a function to return a test-purposed instance of the CustomerVO class.

public function get testCustomer() : CustomerVO
{
	var sampleCustomer : CustomerVO = new CustomerVO();
	sampleCustomer.firstName = "Omer";
	sampleCustomer.lastName = "CUST";
	sampleCustomer.email = "omercust@servebox.yyy"
	sampleCustomer.city = cities[0];
	return sampleCustomer;
}

.. and bind our testCustomer accessor to the source property of our SmartForm :

<form:SmartForm
	id="customerSmartForm"
	source="{testCustomer}"
	styleName="form">

Each SmartForm elements will be filled with the corresponding property of the CustomerVO. In the specific case of the city property, we need to set the propertyForMatch property of the combobox to indicate which of the properties should be used to select the correct index in the data provider.

<elements:SFComboBox id="city" dataProvider="{cities}" labelField="city" propertyForMatch="zipCode"/>

Retrieve a strong-typed object from a SmartForm

In most of case, after user inputs, you should get back the updated values into the strong typed object. Reading the source property of the SmartForm after user inputs will return a DictionaryTable of updated value.
So you can use SmartForm.getSourceAsBasicObject for retrieving the typed object.

public function getUpdatedCustomer() : CustomerVO
{
	// we write a cutom mappingMap for handling Interface -&gt; Class mapping
	var subTypeMappingMap : Dictionary = new Dictionary();
	subTypeMappingMap["org.servebox.sample.value.ICityVO"] = CityVO;
 
	//retrieve values from SmartForm
	var customerOut : CustomerVO = CustomerVO<span style="padding: 0pt; display: inline; font-size: inherit; color: black;">( </span>customerSmartForm.getSourceAsBasicObject<span style="padding: 0pt; display: inline; font-size: inherit; color: black;">( </span>CustomerVO , subTypeMappingMap) );
 
	// trace for checking updated values
	trace("new firstName " + customerOut.firstName );
	trace("new lastName " + customerOut.lastName);
	trace("new email" + customerOut.email );
	trace("new city " + customerOut.city.city );
 
	return customerOut;
}

Note : subTypeMappingMap allow to map specific interfaces on classes.

Simple SmartForm sample

Here you can download sample SmartForm sources ( rar ).
Create a new Flex Project, add SWC & sources of Foundry, then copy the sample on your Project.

Page 1 Page 2

Monday, October 27th, 2008 No Comments by Alexis Desmarais