Building a Flex Type-Ahead Text Input

Exclusive offer: get 50% off this eBook here
Flex 3 with Java

Flex 3 with Java — Save 50%

Develop rich internet applications quickly and easily using Adobe Flex 3, ActionScript 3.0 and integrate with a Java backend using BlazeDS 3.2

$23.99    $12.00
by Keith Lee | March 2010 | Open Source

A Type-Ahead TextInput is feature common to modern day web forms. Quite simply, it is a way to allow the user to enter free text into a field and simultaneously display a list of possible choices based on the text they've already entered. It does so in a way that does not interfere with the users choice of input. In this article by Keith Lee, we will use Flex and build a AutoComplete TextInput.

Here is an example of how google.com implements the type-ahead list using DHTML:

Building a Flex Type-Ahead Text Input

As you can see, once 'type-ahead' is typed into the text field , the user is given a selection of possible search phrases that google is already aware of. My intention with this article is to build a type-ahead list in Flex.

To start, lets narrow down the scope of the application and make it easy to expand on. We'll create an application which is used primarily for searching for fruits.

Our basic Fruit Finder application will consist of a form with a TextInput field. The TextInput field will allow the user to type in a fruit name and will automatically suggest a fruit if one is partially found in our list of fruits.

1. Building a Basic Form

To start, here is what our form looks like:

Building a Flex Type-Ahead Text Input

The XML which creates this user interface is quite simple:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute">
<mx:Panel title="Fruit Finder">
<mx:Form>
<mx:FormHeading label="Fruit Finder"/>

<mx:FormItem label="Fruit Name">
<mx:TextInput id="fruit"/>
</mx:FormItem>

<mx:FormItem>
<mx:Button label="Search"/>
</mx:FormItem>

</mx:Form>
</mx:Panel>
</mx:Application>

You'll notice the normal xml version declaration, the Application tag, a Panel tag and finally the Form tag. Nothing too complicated so far. If you are unfamiliar with the basics of Flex or Forms in Flex, you should take this opportunity to visit Adobe's website to explore them. This XML code gives up 90% of our GUI. In the coming steps will have to define the elements which will make up the fruit list which will appear as a user is typing. Next, we need to define our list of fruits.

2. Adding Data to Our Type Ahead List

Now that we have the beginnings of our GUI, lets start building our fruit list. Thinking ahead for a bit, I know that we will have to display a list of fruits to the user. The simplest Flex control to use for this job is the List Control. We will be dynamically adding the List to the application's display list via ActionScript, but for now we just need to define the data which will be displayed in the list.

We will start by creating adding a Script tag and adding an ArrayCollection to it. You will have to use the import statement to make the ArrayCollection class available to you. Our ArrayCollection constructor is passed an array of fruit names. Here is what the code looks like:

<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;

public var fruitList:ArrayCollection =
new ArrayCollection(['apple',
'orange',
'banana',
'kiwi',
'avocado',
'tomato',
'squash',
'cucumber']);
]]>
</mx:Script>

Normally defining the list of items in this way is not commonly performed. For a real world use, getting this list of items through an XML source is more likely (especially in web applications), but this will work for our demonstration.

Now that our fruit list is defined, we just need to connect it to a type-ahead list which we will create in the next step.

links:
http://livedocs.adobe.com/flex/3/html/help.html?content=databinding_4.html
http://livedocs.adobe.com/flex/3/langref/mx/collections/ArrayCollection.html

3. Triggering the Appearance of Our Type Ahead-List

It is common in modern web applications that the type ahead list appear automatically upon the user typing. We will add this functionality to our application by using the KeyUp event. Simply put, when the user begins typing into our TextInput field we will do the following:

Determine if the type ahead list is already created. For the first key press, there will be no type-ahead list.  In this case we need to create the list, set it's data provider to fruitList (step 2) and add it to the UI. We will also need to position the type ahead list beneath the TextInput field so that the user is properly cued as to what is happening.

To start our implementation of Type-Ahead Text Input, we use the KeyUp event. We change our FormItem tag surrounding the TextInput field to look like this:

<mx:FormItem label="Fruit Name" keyUp="filterFruits(event)">

We then define a filterFruits function like so:

public function filterFruits(event:KeyboardEvent):void{

// if the type ahead list is not present, create it
if(typeAheadList==null){

// create the list and assign the dataprovider
typeAheadList = new List();
typeAheadList.dataProvider = fruitList;

// add the list to the screen
this.addChild(typeAheadList);

}
}

In the above code we are programmatically creating a List control. Immediately assign the data provider to it. Lastly, we add the child to the application.

Our function does everything that we need it to do for a Type-Ahead Text Input with the exception of positioning the type ahead list in the correct place. Here is what our app currently looks like:

Building a Flex Type-Ahead Text Input

We are making progress, but without the correct positioning, our type-ahead list creates a bad user experience. To move this list to the correct location we need to use the localtoGlobal method to translate coordinate systems. This requires a short explanation.

Flex has multiple coordinate systems on the Flash stage that you can make use of for making your controls and components position properly. The first is call the global corrdinate system. This system starts at the upper left hand corner of the Flash stage and extends down and out. The second is called the local coordinate system which starts at the upper left hand corner of a component. There is also a content coordinate system which encompasses a components content. For our purposes we only need to focus on the local and global systems.

link:
http://livedocs.adobe.com/flex/3/html/help.html?content=containers_intro_5.html

Our goal here is to place our list directly beneath the fruit TextInput field. To accomplish this, we must first grabs the coordinates of the fruit TextInput field. Here is the code for retrieving them:

var p1:Point = new Point(fruit.x,fruit.y);

We use the Point type which receives the x and y coordinates of the fruit control. p1 now holds the points in the local coordinate system. You may ask, "what is it local to?". In this case it is local to it's parent container which is the FormItem. In order to convert these points to the global system we need to use to the localToGlobal method:

var p2:Point = fruit_form_item.localToGlobal(p1);

p2 now contains the converted coordinates. Note, we added the id of fruit_form_item to the FormItem Tag which is the parent of our fruit TextInput. From here we can now place the fruit List in the correct place in our application.

typeAheadList.x=p2.x;
typeAheadList.y=p2.y + fruit.height;

// set the width
typeAheadList.width=fruit.width;

Notice above that we added fruit.height to the y value of the typeAheadList. This is necessary to not block the view TextInput field. We are moving it down by n pixels, where n is the height of the TextInput field. We also set the x coordinate of our list so that it is in the correct place. Here is what the final result for this step look like:

Building a Flex Type-Ahead Text Input

Flex 3 with Java Develop rich internet applications quickly and easily using Adobe Flex 3, ActionScript 3.0 and integrate with a Java backend using BlazeDS 3.2
Published: June 2009
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

4. Filter the Items in the List

We now have our list appearing in the correct position, but the items in the list don't match what we are typing. To accomplish this, we need to use the dataProviders filterFunction property. The filter function gives the programmer a change to remove items from the list before they are display to the user. To implement this feature we must set the filterFunc property of the ArrayCollection:

// set the filter function of the data provider
fruitList.filterFunction=filterSelection;

We must then define a function called filterSelection:

public function filterSelection(item:Object):Boolean{
if (String(item).indexOf(fruit.text)!=-1){
return true;
}
return false;
}

The filterSelection function has the job of returning true or false. True if the item should be visible in the the list, false otherwise. The item is question is passed in as a parameter to the filterSelection function. In our conditional, we are checking to see if fruit.text appears in the item being checked using the indexOf method:

String(item).indexOf(fruit.text)!=-1

If the item appears, we return true.

link: http://livedocs.adobe.com/flex/3/langref/String.html#indexOf()

After setting the filterFunction we must call the ArrayCollections refresh() property. This will force Flex to update the ArrayCollection with values that the filterFunction may have removed. Here is what a filtered list looks like:

Building a Flex Type-Ahead Text Input

This filtering algorithm displays any fruit in the list which contains the letter sequence typed in by the user.

5. Adding Mouse and Keyboard controls

We almost have a complete type-ahead TextInput field at this point. We just need to add a few touches which will make the list a little more usable. For instance, when the user clicks on an item in the list, the list should disappear and the clicked on fruit should appear in our TextInput field. Also, we can cancel the type ahead list by hitting the escape button. Another nice touch would be to enable the arrow key to select a fruit from the list. Lets tackle these one at a time.

  1. Clicking on a Fruit - For this one, we need to listen for the click event in our type-ahead list. Once this event is fired, we can simply take the selected item from the list and set the TextInput field to that value. For starters, lets set up the listener.
    // set the mouse click listener
    typeAheadList.addEventListener(MouseEvent.CLICK,selectFruit);

    We can add the above statement in filterFruits function. Here is the definition of the selectFruit:

    public function selectFruit(event:MouseEvent):void{

    // be sure that something is selected
    if (typeAheadList.selectedItem==-1){
    return;
    }

    // set the TextInput
    fruit.text = String(typeAheadList.selectedItem);

    // hide the type ahead list
    typeAheadList.visible=false;

    }

    Lets break down the selectFruit function. First we are checking to see of the typeAheadList has a selected item. Because a user must have clicked on a list item for this function to be called, this condition may never be true. However, there are rare cases in which the item may be clicked on and another action by the user may unselected the item, after the event is in the process of being captured. This conditional is for this rare case.

    The next statement assigns the value of the selected item in the type-ahead list to the TextInput field. And lastly, we make the type ahead list invisible so that the user can continue to fill out the form. It is important to mention that we must now turn the type-ahead list back on when we need it next (by setting the visible property).  Otherwise, it will remain invisible and unusable. The place in the code where we turn the visibility back on is in our filterFruits function.

  2. Cancelling the Type-Ahead with the Escape Key

    Our next order of business is making the type ahead list disappear when the escape key is pressed. The framework for doing this is already in place via our filterFruits function. Because this function is called every time a key press happens, we can take advantage of this situation and wait for the escape key to be pressed. The filterFruits function receives a KeyboardEvent which has a property called keyCode. keyCode is an unsigned integer which represent each key on the keyboard. The important information here is that the Escape key maps to keyCode 27. What we need is a conditional which checks for this keyCode and hides the type-ahead list. Here is what this code looks like:

    // if the key that was pressed is the escape key
    if (event.keyCode==27){

    // show the type ahead list
    typeAheadList.visible=false;

    return;
    }

    Here we are returning early in the filterFruits function after setting the visibility to false.

  3. Arrow key selection of the Fruit

    For this task, we need to move focus from the TextInput field to the type-ahead list. We will catch the down array keyCode much like we did in step one, but this time we will change the keyCode to 40. Add this code to the filterFruits method:

    // give focus to the type ahead list
    if (event.keyCode==40){

    typeAheadList.setFocus();
    typeAheadList.selectedIndex=0;

    return;
    }

    The setFocus Method does the job of setting the focus whicle the selectIndex=0 makes the selects the first item in the type-ahead list.

Other Cool Things

Lastly, there are a few more cool things to we can add to this app. We wont cover them fully here, but we'll describe the methods to implement them. These items aren't essential to a type-ahead list, but they make it easier for developers and end-users alike.

  1. Make the type-ahead list into a custom component - Instead of building an entire application as we did, build a custom component. This will allow other developers to include your work in their apps, leaving implementation details encapsulated in the component.

    You'll need to make sure the data provider can be set by the developer making use of the component. This will allow them to use it for any purposed they'd like.

    link: http://livedocs.adobe.com/flex/3/html/help.html?content=Part1_intro_1.html

  2. Extending the type-ahead List control to accept the Enter Key - Just as we coded the click of the list item to select the fruit, we can do the same with the enter key.  To do this, just define another event listener and accompanying method for the type-ahead list. The function will perform very much like the selectFruit method.
  3. Using an Item Render for the type-ahead List - If your are not familiar with Item Renderers, this is a great opporunity to learn about them. They essentially allow you to change the way data in a list is rendered. For something visual like a list of fruits, it would be simple to display the picture of the fruit along with its name.

    For this implementation, you'll need to know how to embed images in your application or use image urls. You'll also need to know the fundamentals of itemRenders.

    links:
    http://www.adobe.com/devnet/flex/quickstart/using_item_renderers/
    http://livedocs.adobe.com/flex/3/html/help.html?content=embed_4.html

Complete Code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute">
<mx:Panel title="Fruit Finder">
<mx:Form>
<mx:FormHeading label="Fruit Finder"/>
<mx:FormItem label="Fruit Name" id="fruit_form_item"
keyUp="filterFruits(event)">
<mx:TextInput id="fruit"/>
</mx:FormItem>
<mx:FormItem>
<mx:Button label="Search"/>
</mx:FormItem>
</mx:Form>
</mx:Panel>

<mx:Script>
<![CDATA[

import mx.controls.List;
import mx.collections.ArrayCollection;

public var fruitList:ArrayCollection = new
ArrayCollection(['apple',
'orange',
'banana',
'kiwi',
'avocado',
'tomato',
'tagerine',
'pineapple',
'eggplant',
'logan',
'tamarind',
'squash',
'cucumber']);
public var typeAheadList:List = null;

public function filterFruits(event:KeyboardEvent):void{

// if the key that was pressed is the escape key
if (event.keyCode==27){

// show the type ahead list
typeAheadList.visible=false;

return;
}

// give focus to the list
if (event.keyCode==40){

typeAheadList.setFocus();
typeAheadList.selectedIndex=0;

return;
}

// if the type ahead list is not present, create it
if(typeAheadList==null){

// create the list and assign the dataprovider
typeAheadList = new List();
typeAheadList.dataProvider = fruitList;

var p1:Point = new Point(fruit.x,fruit.y);
var p2:Point = fruit_form_item.localToGlobal(p1);

typeAheadList.x=p2.x;
typeAheadList.y=p2.y + fruit.height;

// set the width
typeAheadList.width=fruit.width;

// add the list to the screen
this.addChild(typeAheadList);

// set the filter function of the data provider
fruitList.filterFunction=filterSelection;

// set the mouse click listener
typeAheadList.addEventListener(MouseEvent.CLICK,selectFruit);

}

// update the filter items
fruitList.refresh();

// show the type ahead list
typeAheadList.visible=true;
}

public function selectFruit(event:MouseEvent):void{

// be sure that something is selected
if (typeAheadList.selectedItem==-1){
return;
}

// set the TextInput
fruit.text = String(typeAheadList.selectedItem);

// hide the type ahead list
typeAheadList.visible=false;
}

public function filterSelection(item:Object):Boolean{
if (String(item).indexOf(fruit.text)!=-1){
return true;
}

return false;
}

]]>
</mx:Script>
</mx:Application>

Summary

In this article we used Flex to build a Type-Ahead Text Input. A Type-Ahead TextInput is feature common to modern day web forms. We also covered a few techniques to enhance and expand on the app we built.


If you have read this article you may be interested to view :


Flex 3 with Java Develop rich internet applications quickly and easily using Adobe Flex 3, ActionScript 3.0 and integrate with a Java backend using BlazeDS 3.2
Published: June 2009
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

About the Author :


Keith Lee has been a software developer since graduating college in 1999 with an MS in Systems Administration and Computer Networking. He has developed in many languages, but more recently in Flex/ActionScript and PHP. Presently he is a contributing author for the Developer Shed Network of sites and consults on private projects. Keith currently serves as Senior Software Developer of Special Projects for Ziff Davis Enterprise.

Books From Packt


MySQL Admin Cookbook
MySQL Admin Cookbook

Spring Python 1.1
Spring Python 1.1

Drupal 7 First look
Drupal 7 First look

Magento 1.3 Sales Tactics Cookbook
Magento 1.3 Sales Tactics Cookbook

NHibernate 2.x Beginner's Guide
NHibernate 2.x Beginner's Guide

iReport 3.7
iReport 3.7

JavaFX 1.2 Application Development Cookbook
JavaFX 1.2 Application Development Cookbook

Apache MyFaces 1.2 Web Application Development
Apache MyFaces 1.2 Web Application Development


Your rating: None Average: 5 (3 votes)
Very Nice.. Is this possible by
Very Nice.. Is this possible when the user focus on the text box and popup will appear with the list?
Boris by
Thank you,
Positioning of the look-ahead by
The code fragment var p1:Point = new Point(fruit.x,fruit.y); var p2:Point = fruit_form_item.localToGlobal(p1); typeAheadList.x=p2.x; typeAheadList.y=p2.y + fruit.height; does not position the look-ahead correctly.

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
7
Z
X
N
j
Q
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software