Flex 3 html template and application version

February 10, 2010 by Cedric

To prevent cache problems when you update your flex application you need to pass the application version. I tested a lot of solution and to this day this one seems to be the best to me:

Add the version number as a URL parameter in the html template at 3 places in the code.

<!-- saved from url=(0014)about:internet -->
<html lang="en">

<!--
Smart developers always View Source. 

This application was built using Adobe Flex, an open source framework
for building rich Internet applications that get delivered via the
Flash Player or to desktops via Adobe AIR. 

Learn more about Flex at http://flex.org
// -->

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<!--  BEGIN Browser History required section -->
<link rel="stylesheet" type="text/css" href="history/history.css" />
<!--  END Browser History required section -->

<title>${title}</title>
<script src="AC_OETags.js" language="javascript"></script>

<!--  BEGIN Browser History required section -->
<script src="history/history.js" language="javascript"></script>
<!--  END Browser History required section -->

<style>
body { margin: 0px; overflow:hidden }
</style>
<script language="JavaScript" type="text/javascript">
<!--
// -----------------------------------------------------------------------------
// Globals
// Major version of Flash required
var requiredMajorVersion = ${version_major};
// Minor version of Flash required
var requiredMinorVersion = ${version_minor};
// Minor version of Flash required
var requiredRevision = ${version_revision};
// -----------------------------------------------------------------------------
// -->
</script>
</head>

<body scroll="no">
<script language="JavaScript" type="text/javascript">
<!--
// Version check for the Flash Player that has the ability to start Player Product Install (6.0r65)
var hasProductInstall = DetectFlashVer(6, 0, 65);

// Version check based upon the values defined in globals
var hasRequestedVersion = DetectFlashVer(requiredMajorVersion, requiredMinorVersion, requiredRevision);

if ( hasProductInstall && !hasRequestedVersion ) {
	// DO NOT MODIFY THE FOLLOWING FOUR LINES
	// Location visited after installation is complete if installation is required
	var MMPlayerType = (isIE == true) ? "ActiveX" : "PlugIn";
	var MMredirectURL = window.location;
    document.title = document.title.slice(0, 47) + " - Flash Player Installation";
    var MMdoctitle = document.title;

	AC_FL_RunContent(
		"src", "playerProductInstall",
		"FlashVars", "MMredirectURL="+MMredirectURL+'&MMplayerType='+MMPlayerType+'&MMdoctitle='+MMdoctitle+"",
		"width", "${width}",
		"height", "${height}",
		"align", "middle",
		"id", "${application}",
		"quality", "high",
		"bgcolor", "${bgcolor}",
		"name", "${application}",
		"allowScriptAccess","sameDomain",
		"type", "application/x-shockwave-flash",
		"pluginspage", "http://www.adobe.com/go/getflashplayer"
	);
} else if (hasRequestedVersion) {
	// if we've detected an acceptable version
	// embed the Flash Content SWF when all tests are passed
	AC_FL_RunContent(
			"src", "${swf}?version=1-2-0",
			"width", "${width}",
			"height", "${height}",
			"align", "middle",
			"id", "${application}",
			"quality", "high",
			"bgcolor", "${bgcolor}",
			"name", "${application}",
			"allowScriptAccess","sameDomain",
			"type", "application/x-shockwave-flash",
			"pluginspage", "http://www.adobe.com/go/getflashplayer"
	);
  } else {  // flash is too old or we can't detect the plugin
    var alternateContent = 'Alternate HTML content should be placed here. '
  	+ 'This content requires the Adobe Flash Player. '
   	+ '<a href=http://www.adobe.com/go/getflash/>Get Flash</a>';
    document.write(alternateContent);  // insert non-flash content
  }
// -->
</script>
<noscript>
  	<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
			id="${application}" width="${width}" height="${height}"
			codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
			<param name="movie" value="${swf}.swf?version=1-2-0" />
			<param name="version" value="1-2-0" />
			<param name="quality" value="high" />
			<param name="bgcolor" value="${bgcolor}" />
			<param name="allowScriptAccess" value="sameDomain" />
			<embed src="${swf}.swf?version=1-2-0" quality="high" bgcolor="${bgcolor}"
				width="${width}" height="${height}" name="${application}" align="middle"
				play="true"
				loop="false"
				quality="high"
				allowScriptAccess="sameDomain"
				type="application/x-shockwave-flash"
				pluginspage="http://www.adobe.com/go/getflashplayer">
			</embed>
	</object>
</noscript>
</body>
</html>

Custom Text Component that wrap correctly even when you zoom.

January 12, 2010 by Cedric

Create a new component extending the mx:Text component and that in:


<mx:Text xmlns:mx="http://www.adobe.com/2006/mxml" render="invalidateSize();validateNow();this.mx_internal::getTextField().wordWrap=true"/>

I food this solution here:

http://stackoverflow.com/questions/538199/how-to-get-a-flex-text-control-to-word-wrap

It seems to work but I wonder its impact on performance since it does all those operations on render.

Adobe Flex Layout immitation of common HTML/CSS Layout

January 8, 2010 by Cedric

So you try to convince your boss that Flex can be a perfectly valid choice for a web site but he keeps on telling you the same cliche’.
back button
browser history
accessibility

Here is my answer (flex project):
HTMLLikeLayout rename me .zip

(Sorry for the “.doc rename” thing but wordpress doesn’t allow me to upload zip file. If anybody has a better work around…)

When you test the app, do it on a server (ex: apache)
If you open it from your desktop deeplinking will not work.

There is a wrap problem on the mx:Text component. I posted a solution in a separate post but did not yet update the source of this project. I let you correct it by youself.

Flex nested containers and verticall scrollbar. The solution: use a Canvas

December 8, 2009 by Cedric
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:VBox height="100%" width="100%">
<mx:HBox width="100%" id="firstInnerBox" top="0">
<mx:Label text="Ouside Scroll" width="100%"/>
</mx:HBox>
<mx:Canvas y="30" height="100%" width="100%" verticalScrollPolicy="on" horizontalScrollPolicy="off">
<mx:VBox width="100%" height="2000" backgroundColor="0x00ff00" verticalScrollPolicy="off">
<mx:Label text="Inside Scroll" width="100%"/>
</mx:VBox>
</mx:Canvas>
</mx:VBox>
</mx:Application>

Best way to open and close/dispose of a popup in flex

November 30, 2009 by Cedric

I think that the best way for now…

detailsWindow as a class property so that we can nullify it later.
On close event, dispose then close then nullify for garbage collection.
Any thought on that?

private var detailsWindow:EquipmentDetailsWindow;

private function closeDetailsWindow(evt:CloseEvent):void{

if(detailsWindow==null){
detailsWindow =new EquipmentDetailsWindow();
detailsWindow.addEventListener(CloseEvent.CLOSE,closeDetailsWindow,false,0,true);
detailsWindow.action=action;
detailsWindow.selectedItem=selectedItem;
PopUpManager.addPopUp(detailsWindow,DisplayObject(Application.application),false);
PopUpManager.centerPopUp(detailsWindow);
}else{
var alertError:Alert=Alert.show("window already opened.");
alertError.styleName="alertError";
}
}
private function closeDetailsWindow(evt:CloseEvent):void{
detailsWindow.removeEventListener(CloseEvent.CLOSE,closeDetailsWindow);
detailsWindow.dispose();
PopUpManager.removePopUp(EquipmentDetailsWindow(evt.currentTarget));
detailsWindow=null;
}

Keep a flex window at the size of the stage.

November 18, 2009 by Cedric

If you use stage.height you get incorrect size.
You should use: stage.stageHeight
And listen resize event to adapt the window if browser resize.

private function init():void{
stage.addEventListener(Event.RESIZE,resizeWindow);
}

private function resizeWindow(evt:Event):void {
width=stage.stageWidth-20;
height=stage.stageHeight-20;
}

Don’t forget to remove the event listener when you dispose of the window

Flex php synchronization database server and client

October 2, 2009 by Cedric

I found this article about synchronization:

http://www.databasejournal.com/features/sybase/article.php/3769756/The-Missing-Sync.htm

It doesn’t go into technical details but you can guess what kind of coding will implement this strategies.

I don’t have fancy notifications from my server so I need synchronization strategies.

For instance I have a list of companies in my modelLocator. It doesn’t change really often, it’s not big enough to consider pagination, I don’t want to reload it all (removeAll()) on each user action but yet I don’t want my application to crash or UPDATE corrupt data in case it has been UPDATED or DELETED from another instance of the application.

What I do now is saving in a SESSION the SELECT datetime. When I come back for refreshing the data I SELECT WHERE last_modified>$SESSION['lastLoad']

This way I get only rows modified after I loaded the data (most of the time 0 rows).

Obviously you need to UPDATE last_modified on each INSERT and UPDATE.

For DELETE it’s more tricky. As the guy point out in his article:
“How can we send up a record that no longer exists”

You need to tell flex which item it should delete (say by ID) so you cannot really DELETE on DELETE :)

When a user delete a company you do an UPDATE instead: deleted=1
Then on refresh companies, for row where deleted=1 you just send back the ID to flex so that it makes sure this company isn’t in the model anymore.

Last but not the least, you need to write a function that clean rows where deleted=1 and last_modified is older than … 3days or whatever you think suits your needs.

The good thing is that if a user delete a row by mistake it’s still in the database and you can save it from real delete within 3days.

Case Study: One Array Source For Two ArrayCollection

September 4, 2009 by Cedric

Here the flex project:

OneArrayTwoCollections: Flex Project. Rename it to .zip

My conclusion is that there is very few reasons to copy an ArrayCollection (and it’s source).

If you need 2 views on an Array, for instance:

- one view sort by id to quickly retrieve an object (often the one in your ModelLocator for Cairngorm)
- one view for a dataProvider (different sort and filter can be applyied. often in a custom control such as a filtered DataGrid).

Create 2 ArrayCollections that share the same Array source. When you update the source Array both Collections changes but you can still apply filters and sorts on one collection without modifying the other.

I paste projects comments for google search:

//Just modifying an item properties will not trigger a COLLECTION_CHANGE change event,
//Once you modified an ArrayCollection you need to refresh it
//DataGrids who listen to COLLECTION_CHANGE event will update as well as anything you would have add to the listener
//Listener for the Second Array, refreshing is automatic for the first one
//Removing an Item automatically create an event listener and therefor a refresh
//But only on the target ArrayCollection. Other AC that shares the source should manually Listen to a change event
//Same behavior as removing
//Same behavior as removing and adding
//So if you want to silently update a whole ArrayCollection then take action do not replace the item,
//but update properties and then refresh
//If you need a close reaction to each changes replace the whole item
//If you want to keep options update properties and keep a refresh after each properties update
//Shows you that when an Array is refreshed, it doesn’t refresh only the replaced item that triggered the event,
//it refresh the whole Array even items who wasn’t source of a COLLECTION_CHANGE event because of property update

http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/Flex/Q_24184899.html

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

Flex: keep branches opened after XML dataProvider update

July 24, 2009 by Cedric

The solution is a mix of this tutorial:

http://www.adobe.com/devnet/flex/quickstart/working_with_tree/

And this tutorial:

http://blog.flexexamples.com/2008/01/15/expanding-nodes-in-a-flex-tree-control-using-the-openitems-property/

The first tutorial alone isn’t the right solution when you have a dynamic dataProvider because it’s never exactly the same.

So you have to replace this:
SampleTree.openItems = open;

By this:

var xList:XMLList = dp..node.(hasOwnProperty("@isOpen") && @isOpen == "true");
tree.openItems = xList;

Of course you have to place the @isOpen attribute in your XMLnodes based on openItems contents

Flex: copy a strongly typed object properties into another strongly typed object

July 1, 2009 by Cedric

You can’t use for..in

You have to use flash.utils.describeType

I found the solution there:

http://www.lynchconsulting.com.au/blog/index.cfm/2008/2/6/AS3–Looping-over-properties-of-a-class

My version:


var selectedProduct:Product=(event as AddProductToCartEvent).product;
var quantity:int=(event as AddProductToCartEvent).quantity;

var purchasedProduct:PurchasedProduct=new PurchasedProduct();
//copy prop from selectedProduct into purchasedProduct
var varList:XMLList = flash.utils.describeType(selectedProduct)..accessor;
for(var i:int=0;i<varList.length();i++){
purchasedProduct[varList[i].@name]=selectedProduct[varList[i].@name];
}
//add the quantity
purchasedProduct.quantity=int(quantity);