Archive for the 'AIR' Category

Complex DataGrid filterFunction in Flex/Air

So I need to add robust user controlled filtering options in the application I am working on. This problem has dogged me for a month or two, with my co-workers politely insisting on more and better ways to filter their data. This is completely understandable. When you are staring down a datagrid that is virtually 12 feet long, getting to what you actually want to see is important.

This 2006 article from Bruce Phillips was a big help in the basic concept of multiple input filtering. As is his normal style, it is littered with good references to the source material from his research. This is only considering two filter parameters, however, and I need to consider n parameters, that will probably shift as the needs of the end user grow.

The problem is that each new filtering parameter exponentially increases the complexity of the filterFunction. I can see the pattern, but I haven't quite reached the epiphany that I need to break it down into one of those tight little functions I see real programmers creating (unfortunately, I haven't seen any that address my specific problem).

I thought I'd share my naive, and perhaps confusing filterFunction. It would be great to hear how others approach this problem, and come up with a concise solution.

This function utilizes a TextInput to get text search input from the user. It uses
a series of CheckBoxes for various status condition to generate an array [ 1, 0, 1, 1 ]
to represent their on/off state. It also uses two combo boxes to filter by staff members
as well as clients.

filterFunction:

private function filterCases(item:CaseVO):Boolean
{
	//is the item visible or hidden
	var result:Boolean = false;
	//array of CheckBox.selected property values
       // in handy ones and zeroes. This is returned from a function
       // that takes the actual CheckBoxes as input and gives me this.
	var case_mask:Array = [ 1, 0, 1, 1 ];
        //a couple VOs to verify the selectedItem in the ComboBoxes.
	var client:PersonVO;
	var expert:PersonVO;
	//array iteration variable
	var i:int;
 
	var dateFormat:DateFormatter = new DateFormatter( )
	dateFormat.formatString = 'MM/DD/YY'
 
	//text search
	if ( caseGrid.filter.caseNumInput.text.length > 0 )
	{
		var searchResult:Boolean = ( ( String( '0' + item.case_id.toString() ).search( caseGrid.filter.caseNumInput.text ) >= 0 ) ||
										( item.style.toLowerCase().search( caseGrid.filter.caseNumInput.text.toLowerCase() ) >= 0 )  );
		if( item.description )
		{
			searchResult = searchResult || ( item.description.toLowerCase().search( caseGrid.filter.caseNumInput.text.toLowerCase() ) >= 0 )
		}
 
		if ( item.client_id )
		{
			searchResult = searchResult || ( personProxy.personFromId( item.client_id ).fullName.toLowerCase().search( caseGrid.filter.caseNumInput.text.toLowerCase() ) >= 0 )
		}
 
		if ( item.date_of_accident )
		{
			searchResult = searchResult || ( dateFormat.format(item.date_of_accident).search( caseGrid.filter.caseNumInput.text ) >= 0 )
		}
 
		for ( i = 0; i < case_mask.length; i++ )
		{
			if ( caseGrid.filter.client.selectedItem is PersonVO )
			{
				client = caseGrid.filter.client.selectedItem
 
				//is an expert selected for filtering also?
				if ( caseGrid.filter.expert.selectedItem is PersonVO && searchResult)
				{
					expert = caseGrid.filter.expert.selectedItem;
					//filter for client, expert, and status
					if ( (case_mask[i] && item.status == String( i + 1 ) )
							&& item.client_id == client.id
							&& item.expert_id == expert.id && searchResult  )
						result = true;
				}
				else
				{
					//filter for client and status
					if ( (case_mask[i] && item.status == String( i + 1 ) )
							&& item.client_id == client.id && searchResult  )
						result = true;
				}
			}
			else if ( caseGrid.filter.expert.selectedItem is PersonVO )
			{
				expert = caseGrid.filter.expert.selectedItem;
				for ( i = 0; i < case_mask.length; i++ )
				{
					//filter for expert and status
					 if ( (case_mask[i] && item.status == String( i + 1 ) )
					 		&& item.expert_id == expert.id && searchResult  )
					 	result = true
				}
 
			}
			else
			{
				//filter for client and status
				if ( (case_mask[i] && item.status == String( i + 1 ) )
						&& searchResult  )
					result = true;
			}
		}
	}
	//see if a client is selected for filtering
	else if ( caseGrid.filter.client.selectedItem is PersonVO )
	{
		client = caseGrid.filter.client.selectedItem
		for ( i = 0; i < case_mask.length; i++ )
		{
			//is an expert selected for filtering also?
			if ( caseGrid.filter.expert.selectedItem is PersonVO )
			{
				expert = caseGrid.filter.expert.selectedItem;
				//filter for client, expert, and status
				if ( (case_mask[i] && item.status == String( i + 1 ) )
						&& item.client_id == client.id
						&& item.expert_id == expert.id  )
					result = true;
			}
			else
			{
				//filter for client and status
				if ( (case_mask[i] && item.status == String( i + 1 ) )
						&& item.client_id == client.id  )
					result = true;
			}
		}
	}
	//client isn't selected, check the expert filter.
	else if ( caseGrid.filter.expert.selectedItem is PersonVO )
	{
		expert = caseGrid.filter.expert.selectedItem;
		for ( i = 0; i < case_mask.length; i++ )
		{
			//filter for expert and status
			 if ( (case_mask[i] && item.status == String( i + 1 ) )
			 		&& item.expert_id == expert.id  )
			 	result = true
		}
 
	}
	//finally, filter for the status
	else
	{
		for ( i = 0; i < case_mask.length; i++ )
		{
			//filter for status
			if ( case_mask[i] && item.status == String( i + 1 ) )
				result = true;
		}
	}
 
	return result
}

You can see that I am repeating myself from the top down to the final else statement, cutting the filter parameters down like a layer cake. It works, and is relatively quick on a grid with 1500 or so items. This just screams "REFACTOR ME" every time I see it.

Maximizing your AIR application programatically - stage.nativeWindow.maximize()

Just be sure to call it in an applicationComplete handler. My attempts to call it from creationComplete were resulting in a null object reference.

stage.nativeWindow.maximize()