Thursday, 25 October 2007

Advanced inputfiltering with flex

All the time I have been developing web applications in Java I have build my UI upon some framework like Struts, Tapestry or JSF. All of these frameworks encourage serverside validation and to some extend clientside validation.
I haven't seen a web framework facilitating a prober way to do input filtering. The few times I have implemented an inputfilter I have cooked up some javascript to do the job - normally not that reusable.
I don´t see filtering as a substitution of server side validation, but more as an extension of the user experience where the user gets immediate feedback on her use of an application.

At my current project we build the UI on the flash runtime in flex - and the flex framework really brings some interesting opportunities.

Basic
For basic filtering a restrict property can be set on the TextInput component only allowing a subset of characters as input.
But as soon as your inputfilter depends on already entered text the filtering gets a bit more sophisticated :-)

Advanced
On the TextInput you can listen for a TextEvent that contains the data the user is entering as input but react upon it before it actually hits the input field. That gives you the opportunity to prevent the input from reaching the Textinput component by calling the preventDefault() method on the event if the data isn't allowed for some business rule that you apply.

public class FilterTextInput extends TextInput {

public function FilterTextInput() {
addEventListener(TextEvent.TEXT_INPUT, validateInput);
}

private function validateInput(event : TextEvent) : void {
var actualInput : String = EventUtil.getActualText(event);
trace(actualInput);
//sample filtering securing that all input characters are unique
if (! StringUtil.containsUniqueChars(actualInput)) {
event.preventDefault();
}
}
}

Unfortunately the event only contains the data the user is about to enter. So if you would like to validate the entire input as it would be after when the input is combined with the already entered text, you have to do some work yourself.


EventUtil
I implemented an EventUtil class capable of extracting the actual input as it would look like when the already entered text is combined with the incoming input (the method used in the sample above). The tricky part is to take notice of where in the existing text the new input is about to be entered. The input could replace some existing text and at the same time be pasted into the middle of an existing text.

My utility method looks like this:
public static function getActualText(event : TextEvent) : String {
var input : TextInput = TextInput(event.currentTarget);
var text : String = input.text;
var b : int = input.selectionBeginIndex;
var e : int = input.selectionEndIndex;

var before : String = "";
var after : String = "";
if (e > 0) {
before = text.substring(0, b);
}
if (b < text.length) {
after = text.substring(e, text.length);
}
return before + event.text + after;
}

A simple example illustrating the use of function getActualInput(). The filter prevents the user from entering the same character twice:

No comments: