Hello. I have a new blog.

I've moved this blog to the following URL Kerkness.ca. Thank you for visiting, please update your bookmarks.

Thursday, November 6, 2008

Creating an ItemRenderer in ActionScript

I'm posting this for my benefit as much as anyone else. Here is a very basic ItemRenderer done in ActionScript. Something you might come across when building Flex/Air applications is errand problems with ItemRenderer's not performing as expected, sucking up lots of memory, or throwing errors. Many times these problems can be solved by building your renderer in ActionScript. Here is the source for a basic item renderer which can be scaled up as needed.

package my.renderer
{
 import my.model.DataModel;
 
 import mx.controls.Label;
 import mx.controls.listClasses.IListItemRenderer;
 import mx.controls.listClasses.ListBase;
 import mx.core.UIComponent;
 
 public class ArtListRenderer extends UIComponent implements IListItemRenderer
 {
  public function ArtListRenderer()
  {
   super();
  }
  
  
     [Bindable] public var myData:DataModel = new DataModel();
  
     // Internal variable for the property value.
     private var _data:Object;
     
     // Make the data property bindable.
     [Bindable("dataChange")]
     
     // Define the getter method.
     public function get data():Object {
         return _data;
     }
     
     // Define the setter method, and dispatch an event when the property
     // changes to support data binding.
     public function set data(value:Object):void {
         _data = value;
         
         myData = new DataModel();
         myData.firstname = value.firstname;
         myData.lastname = value.lastname;
                 
         invalidateProperties();
         dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
     }
     
     private var hBox:HBox;
     private var firstnameLabel:Label;
     private var lastnameLabel:Label;
     
     override protected function createChildren():void
     {
      super.createChildren();
      
      hBox = new HBox();
      
      firstnameLabel = new Label();
      lastnameLabel = new Label();
      hBox.addChild( firstnameLabel );
      hBox.addChild( lastnameLabel );
      
     }  
     
     override protected function commitProperties():void
     {
      super.commitProperties();
      
      hBox.horizontalScrollPolicy = 'off';
      hBox.verticalScrollPolicy = 'off';
      
      hBox.percentWidth = 100;
      
      firstnameLabel.text = myData.firstname;
      lastnameLabel.text = myData.lastname;
     }
     
     override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
     {
      super.updateDisplayList(unscaledWidth,unscaledHeight);
      hBox.move(0,0);
      hBox.setActualSize( (unscaledWidth-4), unscaledHeight);
     }
 }
}

Thursday, October 16, 2008

Get PHP dynamic variables inside Flex

Let's say that before you load your flex application you want to pass some dynamic variables from PHP for it to act upon. This can be done pretty easily by slipping the values into PHP's  $_GET array and using Flex's ExternalInterface class to get access to them from inside the Flex app.

Here is an example Flex application which pulls values from the URL Query String.

<mx:Application creationcomplete="init()" layout="absolute" xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
private function init():void
{
  var qstr:String = ExternalInterface.call("window.location.search.substring", 1) as String;
  var qarr:Array = qstr.split('&');

  var pairs:Array;
  for( var i:Number = 0 ; i < qarr.length; i++ ){
     pairs = String( qarr[i] ).split('=');
     if( pairs[0] == 'myName' ){
           myName = pairs[1] as String;
     }
  }
}
]]>
</mx:Script>
<mx:Label text="{myName}" />
</mx:Application>

After you build the application and and Flex Builder has generated the .html file that displays your Flex app  you can rename the file .php and then add the following to the top of the file.

<?php $_GET['myName'] = 'Hi I am Kerk'; ?>

Now if you upload your SWF file and PHP file to a server that is running PHP and you open it in your browser you'll  see your value from PHP displayed in your Flex app.

I don't know if that is a clear explanation or not,  or if this is the best way to pass dynamic variables at load time into your flex application but it works for me.  Comments or alternative solutions welcome.

Friday, October 3, 2008

Force creation of complete ViewStack with creationPolicy when building a form across multiple views : Flex Tip

Here's a tip which might be useful for some people.  Sometimes when building a Flex/Adobe Air interface you may want to have form elements which are built across multiple views of a ViewStack, or TabNavigator.  For example I have a contact form which has some rarely used fields contained in a secondary Tab in my form.

Problem with this approach is that the default value of a Container's creationPolicy is 'auto'.  This means child components are only created when they are needed.  If your user never looks at the extra tabs/view stack layers then these form elements are not available if you need to get values or set values for them.

The Solution is to simple set the creationPolicy for your ViewStack or TabNavigator to 'all'.  This will ensure that all child elements are created up front and are available when needed regardless if the user looks at them or not.

Thursday, October 2, 2008

Flex Tip : Use ObjectUtil for Alpha and Numeric sorting on a DataGrid

If you haven't taken the time to look at the features of Flex's ObjectUtil you should. Inside are a few handy methods which I see getting over looked.

Two of those methods are ObjectUtil.stringCompare and ObjectUtil.numericCompare. These methods make it simple to provide intelegent sorting on DataGrid columns.

Let's say you have a DataGrid that has one column full of ID# and another column full of people's names including both first and last names. Chances are the default sorting abilities of the DataGrid will not properly sort either column. The ID# would be sorted 1,10,11,12... instead of 1,2,3,4,5... and the column of names would sort by the first name and not the last name.

Lucky the DataGridColumn allows you to set a custom sort function via the property sortCompareFunction. For example look at these two DataGridColumns.

<mx:DataGridColumn headerText="ID#" dataField="contactid" sortCompareFunction="sortContactId"/>
<mx:DataGridColumn headerText="Name" dataField="full_name" sortCompareFunction="sortLastName">
 <mx:itemRenderer>
  <mx:Component>
    <mx:Label paddingLeft="5" text="{data.firstname} {data.lastname}" />
  </mx:Component>
 </mx:itemRenderer>
</mx:DataGridGolumn>

The first column is for the ID# of a contact, the second column uses an itemRenderer and displays both the firstname and lastname for the contact. Each column defines it's own sortCompareFunction. Both functions make use of methods from mx.utils.ObjectUtil.

private function sortLastName(obj1:Object,obj2:Object):int
{
   var value1:String = (obj1.lastname == '' || obj1.lastname == null) ? null : new String(obj1.lastname);
   var value2:String = (obj2.lastname == '' || obj2.lastname == null) ? null : new String(obj2.lastname);
   return ObjectUtil.stringCompare( value1, value2, true );
}
private function sortContactId(obj1:Object,obj2:Object):int
{
   var value1:Number = (obj1.contactid == '' || obj1.contactid == null) ? null : new Number(obj1.contactid);
   var value2:Number = (obj2.contactid == '' || obj2.contactid == null) ? null : new Number(obj2.contactid);
   return ObjectUtil.numericCompare( value1, value2 );
}

These functions are pretty straight forward.  sortLastName function uses stringCompare method to compare the lastname from two objects. The flag 'true' is set to make sure that the comparison is case insensitive. The sortContactId function uses the numericCompare method to compare the contactid from two objects.

Wednesday, October 1, 2008

Flex Component : ButtonPanel puts a button in your panel header

It's always a joy when you need something and after writing a few lines of code, you have it. Such is the beauty of nice extendable components and the Flex architecture.

Today I needed a panel which had a button in the top right corner of the header. Where the 'status' text is normally is. I wanted to put a 'save' button there. One problem, the mx.containers.Panel component does not have a button in the header.

Solution. Make a new component which extends all the functions of the Panel and stick a button in the top corner where I want.

Click here to see a demo

Click here to view the source

My new ButtonPanel component extends Panel and adds 2 new properties and 1 event.

Properties

buttonLabel : String - Defines the label for the button in the top corner
buttonPadding : Number - Defines how much padding to provide in the header

Event

buttonClick : Event - Dispatched when the button is clicked

Flex Tip: ArrayCollection replace an item with setItemAt

One thing which might not be clear when working with ArrayCollections in Flex is how to replace an item in the collection.  The methods getItemAt and addItemAt are pretty clear in their purpose, but one method which I found myself over looking is the setItemAt method which could be more aptly named replaceItemAt as that is exactly what it does.  It replaces an item in the collection.

So next time you're looking to replace an item in an ArrayCollection and wishing there was a replaceItemAt method, look no further than setItemAt

Saturday, September 13, 2008

PHP json_encode() and Flex JSON.decode Gotcha

I came across a little 'gotcha' when trying to get Flex JSON.decode to properly decode a JSON string I was producing with PHP.

The one of the elements in the string contained ' &quote; ' which when being encoded by php and sent as a HTTPService response back to my Flex application was being converted back into double quotes ( " ). When Flex JSON.decode attempted to decode the string it would silently fail. No errors or anything.

In my case my JSON string was already pretty complicated with lots of variable content so it took a while to figure this out. Hopefully someone else finds this post and saves themselves some time.