Multi-file Upload in FLEX - apache-flex

Is there a FLEX component that uploads multiple files in a multipart/form-data POST request?

You can use FileReference (1 file) or FileReferenceList (multiple files) for uploading multiple files.
Maybe you can find some inspiration on this page: http://soegianto.com/blog/2008/07/multiple-file-upload-with-flex-and-php/

If you're looking for a quick example using Flex and .NET:
Uploader.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="initApp()" >
<mx:Script>
<![CDATA[
import flash.net.URLRequest;
private var __file:FileReference;
private function initApp():void
{
__file = new FileReference();
__file.addEventListener(Event.SELECT , __fileSelectionHandler);
__file.addEventListener(Event.COMPLETE , __completeHandler );
__file.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, __uploadDataHandler );
}
private function __onBrowse():void
{
try
{
var success:Boolean = __file.browse();
}
catch (error:Error)
{
__status__.text = "Status: Unable to open browse dialog";
}
}
private function __onUpload():void
{
// replace with URL to your upload handler - THIS ONE WON'T WORK!!!!
var myRequest:URLRequest = new URLRequest("http://<url>/Uploader.ashx");
myRequest.method = URLRequestMethod.POST;
try
{
__file.upload(myRequest);
__status__.text = "Status: Now uploading " + __file.name + " ... ";
}
catch (error:Error)
{
__status__.text = "Status: Error uploading " + __file.name;
}
}
private function __fileSelectionHandler(_e:Event):void
{
__upload__.enabled = true;
__uploadFile__.text = __file.name;
__status__.text = "Status: Click 'Upload' to begin file upload";
}
private function __completeHandler(_e:Event):void
{
// nothing currently done in this handler - experiment and have fun :)
}
private function __uploadDataHandler(_e:DataEvent):void
{
var myResult:XML = new XML(_e.data);
__status__.text = "File Upload Complete \n" + myResult.toString();
}
]]>
</mx:Script>
<mx:TextArea width="300" height="90" id="__status__" wordWrap="true" editable="false" enabled="true" text="Status: Select file to upload"/>
<mx:HBox width="300" height="20" horizontalAlign="center">
<mx:TextInput id="__uploadFile__" editable="false" enabled="true"/>
<mx:Button label="Browse" id="__browse__" enabled="true" click="__onBrowse()"/>
</mx:HBox>
<mx:HRule width="300"/>
<mx:Button label="Upload" id="__upload__" enabled="false" click="__onUpload()"/>
</mx:Application>
Uploader.ashx:
<%# WebHandler Language="C#" Class="Uploader" %>
using System.IO;
using System.Web;
using System.Web.Configuration;
public class Uploader : IHttpHandler
{
public void ProcessRequest( HttpContext _context )
{
// not very elegant - change to full path of your upload folder (there are no upload folders on my site)
string uploadDir = "C:\\<path to upload folder>\\";
if (_context.Request.Files.Count == 0)
{
_context.Response.Write("<result><status>Error</status><message>No files selected</message></result>");
return;
}
foreach(string fileKey in _context.Request.Files)
{
HttpPostedFile file = _context.Request.Files[fileKey];
file.SaveAs(Path.Combine(uploadDir, file.FileName));
}
_context.Response.Write("<result><status>Success</status><message>Upload completed</message></result>");
}
public bool IsReusable
{
get { return true; }
}
}
Note that this is basically just a transcription of the Algorithmist's Post from '07

You can only upload one at a time with the Flex classes. If you want more than one in the same upload request, try MultipartURLLoader:
http://code.google.com/p/in-spirit/wiki/MultipartURLLoader

Related

Flex/AIR/Actionscript/Mobile File.writeObject/readObject always generates null w/no errors generated

I've been trying for several days just to get a simple writeObject/readObject functionality in my project. No matter what I do, the result is, a) a file is created and b) when I try to read that file back in, it is null.
My project targets iOS on an iPad device, but this stripped down version targets the AIR simulator for an iPad.
I've previously had every single available event registered and traced, but they never accomplished anything so I removed them for the sake of keeping the problem simple.
Even if I use a generic object with a single string property set instead of my value object, it still reads back (and I assume, writes out) as null.
Here is my trace return followed by the code used in the project:
[SWF] PictureToolsOnTheMoveMakeItDev.swf - 2,644,533 bytes after decompression
PictureToolsOnTheMoveMakeItDev FUNCTION creationCompleteHandler
file.resolvePath(filename).nativePath: C:\Users\cepelc\AppData\Roaming\org.PictureTools.Apps.PictureToolsOnTheMoveMakeItDev.debug\Local Store\User00100\Photos\00100-1358359285139.PTotmImageVO
PictureToolsOnTheMoveMakeItDev FUNCTION saveImageToLibrary
FileSerializer FUNCTION writeObjectToFile()
FileStream.open(write) TRY
FileStream.open(write) FINALLY
FileStream.writeObject(ptotmImageVO) TRY
FileStream.writeObject(ptotmImageVO) FINALLY
FileStream.close()
PictureToolsOnTheMoveMakeItDev FUNCTION readImageFromLibrary
FileSerializer FUNCTION readObjectFromFile(C:\Users\cepelc\AppData\Roaming\org.PictureTools.Apps.PictureToolsOnTheMoveMakeItDev.debug\Local Store\User00100\Photos\00100-1358359285139.PTotmImageVO)
file.exists: true
FileStream.open(read) TRY
FileStream.open(read) FINALLY
FileStream.readObject() TRY
FileStream.readObject() FINALLY
FileStream.close()
FileSerializer FUNCTION readObjectFromFile -- ptotmImageVO -- null
ptotmImageVO: null
[Unload SWF] PictureToolsOnTheMoveMakeItDev.swf
Below is my application MXML:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" applicationDPI="240"
xmlns:c="components.*"
creationComplete="creationCompleteHandler()">
<s:layout>
<s:VerticalLayout verticalAlign="middle" horizontalAlign="center" />
</s:layout>
<fx:Script>
<![CDATA[
import classes.FileSerializer;
import vo.PTotmImageVO;
private var ptotmImageVO:PTotmImageVO;
private var fileSerializer:FileSerializer = new FileSerializer();
private var file:File = File.applicationStorageDirectory;
private var filename:String;
protected function creationCompleteHandler():void
{
trace("PictureToolsOnTheMoveMakeItDev FUNCTION creationCompleteHandler");
ptotmImageVO = new PTotmImageVO();
ptotmImageVO.userid = "00100";
ptotmImageVO.description = "TestPuppyBunnyThingy";
ptotmImageVO.timestamp = new Date().getTime();
ptotmImageVO.type = "PictureTools - On The Move - Photo Entity";
filename = ptotmImageVO.userid+"-"+ptotmImageVO.timestamp+".PTotmImageVO";
file = file.resolvePath("User00100");
if(file.exists && !file.isDirectory)
{
file.deleteFile();
}
file.createDirectory();
file = file.resolvePath("Photos");
if(file.exists && !file.isDirectory)
{
file.deleteFile();
}
file.createDirectory();
trace(" file.resolvePath(filename).nativePath: "+file.resolvePath(filename).nativePath);
saveImageToLibrary();
} // end FUNCTION creationCompleteHandler
protected function saveImageToLibrary():void
{
trace("PictureToolsOnTheMoveMakeItDev FUNCTION saveImageToLibrary");
fileSerializer.writeObjectToFile(ptotmImageVO, file.resolvePath(filename).nativePath);
readImageFromLibrary();
} // end FUNCTION saveImageToLibrary
protected function readImageFromLibrary():void
{
trace("PictureToolsOnTheMoveMakeItDev FUNCTION readImageFromLibrary");
ptotmImageVO = fileSerializer.readObjectFromFile(file.resolvePath(filename).nativePath) as PTotmImageVO;
trace(" ptotmImageVO: "+ptotmImageVO);
} // End FUNCTION readImageFromLibrary
]]>
</fx:Script>
</s:Application>
FileSerializer.as class
package classes
{
import flash.errors.IOError;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import vo.PTotmImageVO;
public class FileSerializer
{
private var fileStream:FileStream = new FileStream();
private var file:File;
public function FileSerializer()
{
} // End CONSTRUCTOR FileSerializer
public function writeObjectToFile(ptotmImageVO:PTotmImageVO, fname:String):void
{
trace("FileSerializer FUNCTION writeObjectToFile()");
file = new File(fname);
try
{
trace(" FileStream.open(write) TRY");
fileStream.open(file, FileMode.WRITE);
}
catch (e:SecurityError)
{
trace(" FileStream.open(write) CATCH SecurityError "+e);
}
finally
{
trace(" FileStream.open(write) FINALLY");
}
try
{
trace(" FileStream.writeObject(ptotmImageVO) TRY");
fileStream.writeObject(ptotmImageVO);
}
catch (e:IOError)
{
trace(" FileStream.writeObject(ptotmImageVO) CATCH IOError "+e);
}
finally
{
trace(" FileStream.writeObject(ptotmImageVO) FINALLY");
}
fileStream.close();
trace(" FileStream.close()");
} // End FUNCTION writeObjectToFile
public function readObjectFromFile(fname:String):PTotmImageVO
{
trace("FileSerializer FUNCTION readObjectFromFile("+fname+")");
var ptotmImageVO:PTotmImageVO;
file = file.resolvePath(fname);
trace(" file.exists: "+file.exists);
if(file.exists)
{
try
{
fileStream.open(file, FileMode.READ);
trace(" FileStream.open(read) TRY");
}
catch (e:SecurityError)
{
trace(" FileStream.open(read) CATCH SecurityError "+e);
}
finally
{
trace(" FileStream.open(read) FINALLY");
}
try
{
trace(" FileStream.readObject() TRY");
ptotmImageVO = fileStream.readObject() as PTotmImageVO;
}
catch (e:IOError)
{
trace(" FileStream.readObject() CATCH IOError "+e);
}
finally
{
trace(" FileStream.readObject() FINALLY");
}
fileStream.close();
trace(" FileStream.close()");
trace(" FileSerializer FUNCTION readObjectFromFile -- ptotmImageVO -- "+ptotmImageVO);
return ptotmImageVO;
}
else
{
return null;
}
} // End FUNCTION readObjectFromFile
} // End CLASS FileSerializer
} // End PACKAGE classes
PTotmImageVO.as value object
package vo
{
import flash.display.BitmapData;
[remoteClass(alias="PTotmImageVO")]
public class PTotmImageVO
{
public var userid:String;
public var thumbnail:BitmapData;
public var image:BitmapData;
public var timestamp:Number;
public var description:String;
public var type:String;
public function PTotmImageVO()
{
} // End Constructor PTotmImageVO
} // End Class PTotmImageVO
} // End Package vo
Resolved. The Metadata tag is [RemoteClass... not [remoteClass as was shown in the example code I began working from. As the compiler doesn't check these tags, and making up your own tag isn't technically an error, there will -NEVER- be any diagnostic data from which to work.

flex: list of files

I'm making a Flex mobile application with Flash Builder 4.6 And I have a question for a function I want to implement.
I have a declaration:
<s:HTTPService id="getXMLFile" fault="trace('No CCR found!')" result="getPatient(event)"
resultFormat="object" url="assets/CCR1.xml"/>
But this only points to one file and this has to be typed in.
Already now how to change the url and do the request again.
getXMLFile.url = "assets/CCR2.xml";
getXMLFile.send();
But I want to have some sort of function to check all the files in the "assets" package and to this code part for each file. So I want something like this:
for(var file:string in assets) {
getXMLFile.url = "assets/" + file;
getXMLFile.send();
}
Thx
<fx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
//Create one xml having all URL, which you can parse on Flex side. Collect all URL in Array (ex:- fileArray),
//than you can loop it. You can call parseXML on creationComplete of your application.
private var fileArray:Array = new Array();
private var count:int = 0;
//Collect all URL in this fileArray.
private function parseXML():void
{
//parse XML and puch URL in fileArray
// xmlLength var is xml length.
var xmlLength:int;
for(var i:int;i<xmlLength ;i++)
{
var URL:String = "";
//URL = parse Value from XML;
fileArray.push(URL);
}
sendRequest();
}
private function sendRequest():void
{
getXMLFile.url = fileArray (count);
getXMLFile.send();
}
private function getPatient(event:ResultEvent):void
{
//If Success request for Next Patient
requestForNextPatient();
}
private function faultHandler(event:FaultEvent):void
{
//If fault request for Next Patient
requestForNextPatient();
}
private function requestForNextPatient():void
{
//Request for Next Patient
count++;
if(count > fileArray.length)
sendRequest():
}
]]>
</fx:Script>
<s:HTTPService id="getXMLFile" fault="faultHandler(event)" result="getPatient(event)" resultFormat="object" />
This may help you.....

How to get a view to read data

I need to convert a lot of stuff in my profession - so I'm building a conversion tool for my phone with some of the conversions I use a lot.
Now, I want to be able to build this properly. So far, here's my code:
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="Length">
<fx:Script>
<![CDATA[
protected function button1_clickHandler(event:MouseEvent):void
{
var Result:String;
var Finish:Number;
var Start:Number = parseFloat(Input.text);
Finish = Start * convert.selectedItem.data;
Result = String(Finish);
answer.text = Result;
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:TextInput id="Input" x="20" y="46"/>
<s:SpinnerListContainer x="140" y="122" width="200" height="200">
<s:SpinnerList id="convert" height="100%" width="100%" labelField="label" selectedIndex="1">
<s:ArrayList>
<fx:Object label="km to mi" data="0.62137119224"></fx:Object>
<fx:Object label="km to yd" data="1093.6132983 "></fx:Object>
<fx:Object label="km to ft" data="3280.839895"></fx:Object>
<fx:Object label="km to in" data="39370.07874"></fx:Object>
<fx:Object label="km to m" data="1000"></fx:Object>
<fx:Object label="km to cm" data="100000"></fx:Object>
<fx:Object label="km to mm" data="1000000"></fx:Object>
</s:ArrayList>
</s:SpinnerList>
</s:SpinnerListContainer>
<s:Label id="answer" x="66" y="533" width="348" text="Answer"/>
<s:Button x="66" y="377" width="338" click="button1_clickHandler(event)" label="Button"/>
</View>
As you can see, I'm going to run into some problems with this:
1) Everything is hard-coded, and if I want to add, remove or change the elements in the array, it's going to be a bit of a pain.
2) I have a view that is essentially the same for my volume and weight conversions. With the same problem.
What I'd like to do, but I'm having some trouble understanding, is getting all that hard-coded stuff in one place and having the same view show it based on my previous view which is just a plain, hard-coded list.
I'm thinking of something like an xml sheet, and adding a category = "length" or category = "weight" element to the objects, so that I can show the category from the xml in the List, then when I click "length" it reads the label + data from this list. Is that a good solution? And how exactly do I get the selectedItem to remember which part of the xml list the view should be populated from?
Would it be better to have several xml files? But that still would mean I have to update a whole bunch of places when I need it.
Basically, I need assistance with:
So - Now the question is two-fold:
1) How to keep a connection to xml/db open across multiple views?
2) How to populate the end-view from information from the db?
Thanks for the advice and help.
I just finished an app about a month ago that's uses what I think would be the most "Flex"ible solution. (hehehehe)
(First Answer:)
If you're into/familiar with (good) database design, you could design a SQLite db that allows you to add to & modify all the data you're working with.
(If not, I'd recommend either:
http://www.amazon.com/The-Art-SQL-Stephane-Faroult/dp/0596008945/ref=sr_1_14?s=books&ie=UTF8&qid=1336262973&sr=1-14
or
http://www.amazon.com/SQL-Demystified-Andrew-Oppel/dp/0072262249/ref=sr_1_1?s=books&ie=UTF8&qid=1336263052&sr=1-1
...this post is going to take longer than I anticipated! hehehhehee ;P =D )
Basically what it'd be is:
Tables for categories (e.g. Volume, Length, etc.) and another for specific name/value pairs ("km to mi" = 0.62137119224 [each in separate columns]) with a category id column to.
Then on your home page you have your init() create an DAO (Data Access Object [research if you don't know already]) for the categories table, then fetch the categories into an ArrayCollection and set it as the dataProvider on your category list (on the home view -- or wherever).
(Second Answer:)
Have your change handler for the category list grab the selectedItem and pass it as the second param in navigator.pushView(). That will send the VO (Value Object -- another to research if you don't know it) to the new View as the "data" property.
In the "pushed view," use your creationComplete handler to "catch" (use) the data variable, which will contain the category's name and id. Create a new DAO for the values table and then use the data.id value to load all your values with that category id. Then set your new ArrayCollection as the dataProvider of your value list.
Then create another View for choosing values to edit in the same way. Except the final view in that "flow" would be a form with inputs for category, name, & value (with save & cancel buttons) that would get populated with the appropriate data too. (Note: use a category DAO to get the names of the categories so that the category names & ids are available if you change a category.
...Then it's just a matter of implementing insert & update methods on that View and the SQL & methods needed in each DAO.
You can use Lita (http://www.dehats.com/drupal/?q=node/58) build, design, pre-populate your database.
...I may come back with some nice example code/files (if I remember)
I made some examples for those who were reading and hoped I would...
//////////////////////////////////////
//VO (Value Object)
//Category.as
//////////////////////////////////////
package dao
{
[Bindable]//Makes all public properties bindable
public class Category
{
import mx.collections.ArrayCollection;
public var id:int = -1;
public var categoryName:String;
private var categoryDao:CategoryDAO;
public function Category() {}
public function get exists():Boolean {
return this.id > -1;
}
//"Super" convenient methods
//Not really part of Value Objects / Value Object Pattern
//May actually be a bad practice if you have many VO instances,
//you have the potential for a DAO instance in each
//when only one instance could be used.
public function insert():void {
if( !categoryDao ){ categoryDao = new CategoryDAO;}
categoryDao.insert( this );
}
public function update():void {
if( !categoryDao ){ categoryDao = new CategoryDAO;}
categoryDao.update( this );
}
public function deleteRow():void {
if( !categoryDao ){ categoryDao = new CategoryDAO;}
categoryDao.deleteRow( this );
}
}
}
//////////////////////////////////////
//DAO (Data Access Object)
//CatagoryDAO.as
//////////////////////////////////////
package dao
{
import flash.data.SQLConnection;
import flash.data.SQLStatement;
import flash.filesystem.File;
import mx.collections.ArrayCollection;
public class CategoryDAO
{
public static var _sqlConnection:SQLConnection;
public var failed:Boolean;
public var errorMessage:*;
public function CategoryDAO() {
}
public function getAll():ArrayCollection
{
var sql:String = "SELECT * FROM categories"
+ " ORDER BY categoryName ASC";
var stmt:SQLStatement = new SQLStatement();
stmt.sqlConnection = sqlConnection;
stmt.text = sql;
stmt.execute();
var result:Array = stmt.getResult().data;
if( result ){
var list:ArrayCollection = new ArrayCollection();
for (var i:int=0; i < result.length; i++){
list.addItem( buildVO( result[i] ) );
}
return list;
} else {
return null;
}
}
public function getByCategoryId(id:int):Category
{
var sql:String = "SELECT * FROM categories WHERE id=?";
var stmt:SQLStatement = new SQLStatement();
stmt.sqlConnection = sqlConnection;
stmt.text = sql;
stmt.parameters[0] = id;
stmt.execute();
var result:Array = stmt.getResult().data;
if( result && result.length == 1 ){
return buildVO( result[0] );
} else {
return null;
}
}
public function insert(category:Category):void
{
var sql:String =
"INSERT INTO categories ( categoryName )" +
" VALUES ( :name )";
var stmt:SQLStatement = new SQLStatement();
stmt.sqlConnection = sqlConnection;
stmt.text = sql;
stmt.parameters[":name"] = category.categoryName;
this.execute( stmt );
}
public function update(category:Category):void
{
var sql:String =
"UPDATE categories" +
" SET categoryName = :name" +
" WHERE id = :id";
var stmt:SQLStatement = new SQLStatement();
stmt.sqlConnection = sqlConnection;
stmt.text = sql;
stmt.parameters[":name"] = category.categoryName;
stmt.parameters[":id"] = category.id;
this.execute( stmt );
}
public function deleteRow(category:Category):void {
var sql:String =
"DELETE FROM categories" +
" WHERE id = :id";
var stmt:SQLStatement = new SQLStatement();
stmt.sqlConnection = sqlConnection;
stmt.text = sql;
stmt.parameters[":id"] = category.id;
this.execute( stmt );
}
protected function execute(stmt:SQLStatement):void {
try {
stmt.execute();
} catch(error:Error) {
this.failed = true;
this.errorMessage = error.message;
}
}
protected function buildVO(o:Object):Category
{
var category:Category = new Category();
category.id = o.id;
category.categoryName = o.categoryName;
return category;
}
public function get sqlConnection():SQLConnection
{
if (_sqlConnection) return _sqlConnection;
var file:File =
File.documentsDirectory.resolvePath(DbUtility.DB_FILE_NAME);
var fileExists:Boolean = file.exists;
_sqlConnection = new SQLConnection();
_sqlConnection.open(file);
return _sqlConnection;
}
}
}
//////////////////////////////////////
//CategoryView.mxml
//////////////////////////////////////
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:dao="dao.*"
opaqueBackground="#111111"
title="All Categorys"
creationComplete="init()">
<fx:Script>
<![CDATA[
import dao.DbUtility;
import dao.DropPoint;
import dao.Category;
import dao.CategoryDAO;
protected var dbVerifyUtil:DbUtility
protected function init():void
{
dbVerifyUtil = new DbUtility;
dbVerifyUtil.confirmDb();
if( dbVerifyUtil.dbExists() ){
var categorysDAO:CategoryDAO = new CategoryDAO;
categoryList.dataProvider = categorysDAO.getAll();
}
}
protected function categorySelected():void
{
navigator.pushView( CategoryListView,
categoryList.selectedItem );
}
protected function newCategory():void
{
navigator.pushView( EditCategoryView );
}
protected function viewCategory():void
{
navigator.pushView( CategoryListView,
categoryList.selectedItem );
}
]]>
</fx:Script>
<s:List id="categoryList"
left="10" right="10" top="10" bottom="85"
change="viewCategory()"
dataProvider="{data}"
itemRenderer="irs.CategoryIR">
</s:List>
<s:Button label="Add Category"
left="104" bottom="10" height="43"
click="newCategory()"/>
<s:Label text="Touch a category to view or edit it."
y="326" horizontalCenter="0"/>
</s:View>
//////////////////////////////////////
//CategoryListView.mxml
//////////////////////////////////////
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:dao="dao.*"
creationComplete="init()">
<fx:Script>
<![CDATA[
import dao.Value;//Value VO
import dao.Category;//Category VO
import mx.collections.ArrayCollection;
import spark.events.IndexChangeEvent;
private var category:Category;
protected function init():void
{
var category:Category = data as Category;
listValues.dataProvider =
valueDAO.getByCategoryId(
category.id );
}
protected function editValue():void
{
navigator.pushView( EditValueView,
listValues.selectedItem );
}
protected function editCategory():void
{
navigator.pushView( EditCategoryView, category );
}
]]>
</fx:Script>
<s:viewMenuItems>
<s:ViewMenuItem label="Edit Category"
click="editCategory()"
icon="#Embed('assets/edit.png')"/>
<s:ViewMenuItem label="Add Location"
click="addLocation()"
icon="#Embed('assets/plus.png')"/>
</s:viewMenuItems>
<s:List id="listValues"
left="10" right="10" top="10" bottom="60"
labelFunction="labelValue"
change="editValue()"
itemRenderer="irs.ValueIR">
</s:List>
</s:View>
Have you looked at using resource bundles? Or LSOs?
When persisting Flex data, you have 4 main options.
Relational Databases (seems like overkill here)
XML (which you already seem comfortable with)
Resource Bundles
Local Shared Objects
The LSO example, linked above (#4), gives a potential solution to your question:
... how exactly do I get the selectedItem to remember which part of the xml list the view should be populated from?
snippet:
public function initApp():void {
mySO = SharedObject.getLocal("mydata");
if (mySO.data.visitDate==null) {
welcomeMessage = "Hello first-timer!"
} else {
welcomeMessage = "Welcome back. You last visited on " +
getVisitDate();
}
}
private function getVisitDate():Date {
return mySO.data.visitDate;
}
private function storeDate():void {
mySO.data.visitDate = new Date();
mySO.flush();
}
private function deleteLSO():void {
// Deletes the SharedObject from the client machine.
// Next time they log in, they will be a 'first-timer'.
mySO.clear();
}
I'd recommend using either a combination of XML and LSOs or resource bundles and LSOs--where your XML/RB stores non-changing, static data and your LSO keeps track of dynamic data (view settings, etc).
Data retrieve
As I understand your problem, you don't have to keep a connection open.
Retrieve the data from your database
Parse and store the retrieved
data in a collection ideally an ArrayCollection (you don't want to
handle XML based objects, they're fine when manipulating String
values, but they instantly become a pain in the .as when you want to
perform type-conversion and advanced add and remove operations)
Generic conversion
Then, with binding as in the following sample, all you have to do is always have two conversions when you convert a value from an unit to another :
conversion into a defined unit (ideally the SI unit, as in my
example)
conversion from the SI unit to the desired unit.
Sample
<?xml version="1.0" encoding="utf-8"?>
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="400" minHeight="300">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var unitz:ArrayCollection = new ArrayCollection([
{
label:"Length",
units: new ArrayCollection([
{label: "meter", toSI: 1},
{label: "kilometer", toSI: 1000},
{label: "inch", toSI: 0.0254}
])
},
{
label:"Temperature",
units: new ArrayCollection([
{label: "kelvin", toSI: 1},
{label: "celsius", toSI: 274.15},
{label: "farenheit", toSI: 255.927778}
])
}
]);
private function resetFields():void
{
fromValue.text = "";
toValue.text = "";
}
private function onInputChange():void
{
var fu:Object = fromUnitCB.selectedItem;
var tu:Object = toUnitCB.selectedItem;
toValue.text = (Number(fromValue.text)*fu.toSI/tu.toSI).toString();
}
]]>
</fx:Script>
<s:HGroup width="100%" height="40">
<s:ComboBox
id="categoryCB"
dataProvider="{unitz}"
change="resetFields()"
/>
<s:ComboBox id="fromUnitCB" dataProvider="{categoryCB.selectedItem.units}"/>
<s:ComboBox id="toUnitCB" dataProvider="{categoryCB.selectedItem.units}"/>
</s:HGroup>
<s:HGroup width="100%">
<s:TextInput id="fromValue" change="onInputChange()"/>
<s:Label id="toValue"/>
</s:HGroup>
</s:Application>

IconItemRender In Flex 4.5.1 With Dynamically Called Icon

Alright, simple item render, or so I thought... I have an some data loaded into a list, one of the fields is project_Type. It's a string that either says "RESIDENTIAL" or "COMMERCIAL". And based on that string, I just want to display a little house, or a little office building as the icon. So, I cobbled together my itemrender as follows:
<?xml version="1.0" encoding="utf-8"?>
<s:IconItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
labelField="dateAdded"
messageField="builder_Name"
iconFunction="myiconfunction"
iconWidth="48" iconHeight="48"
decorator="#Embed('assets/Right-48x48.png')" >
<fx:Script>
<![CDATA[
// iconFunction="myiconfunction"
// iconWidth="32" iconHeight="32" -->
private function myiconfunction(data:Object):String{
var type:String;
var projectType:String = (data != null) ? data.project_Type : "QUESTION";
if (projectType == "RESIDENTIAL") {
type = "assets/House-48x48.png";
}
else if (projectType == "COMMERCIAL") {
type = "assets/Commercial-48x48.png";
}
else if (projectType == "QUESTION") {
type = "assets/Question-48x48.png";
}
return type;
}
]]>
</fx:Script>
</s:IconItemRenderer>
So, pretty straight forward there... But know custom icon.... No errors either though. What am I doing wrong guys?
Update
So, I changed the item renderer to the following, and still no icon... I've double checked the data and RESIDENTIAL and COMMERCIAL are both being passed...
<?xml version="1.0" encoding="utf-8"?>
<s:IconItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
labelField="dateAdded"
messageField="builder_Name"
iconFunction="myiconfunction"
iconWidth="48" iconHeight="48"
decorator="#Embed('assets/Right-48x48.png')" >
<fx:Script>
<![CDATA[
[Embed(source="assets/House-48x48.png")]
public var residentialClass:Class;
[Embed(source="assets/Commercial-48x48.png")]
public var commercialClass:Class;
[Embed(source="assets/Question-48x48.png")]
public var questionClass:Class;
private function myiconfunction(data:Object):Object
{
var projectType:String = (data != null) ? data.project_Type : "QUESTION";
if (projectType == "RESIDENTIAL") {
return residentialClass;
}
else if (projectType == "COMMERCIAL") {
return commercialClass;
}
return questionClass;
}
]]>
</fx:Script>
</s:IconItemRenderer>
But alas, nothing...
These Mods usually work for me.
<?xml version="1.0" encoding="utf-8"?>
<s:IconItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
labelField="dateAdded"
messageField="builder_Name"
iconFunction="myiconfunction"
iconWidth="48" iconHeight="48"
decorator="#Embed('assets/Right-48x48.png')" >
<fx:Script>
<![CDATA[
private function myiconfunction(data:Object):String
{
var s:String = data.project_Type; //capture data for case match
switch (s.toUpperCase()) {
case "RESIDENTIAL":
return 'assets/House-48x48.png';
break;
case "COMMERCIAL":
return 'assets/Commercial-48x48.png';
break;
default:
return 'assets/Question-48x48.png';
break;
}
}
]]>
</fx:Script>
</s:IconItemRenderer>
Are you sure your icon is ever getting called? Are you sure the path to the icon is correct? Are you sure the icon assets are being embedded in your compiled app?
Instead of returning the path to the object from your function, I'd probably return a class object, not a string value. In half psuedo-code, something like this, which would be inside the renderer:
<fx:Script>
[Embed(source="assets/House-48x48.png")]
public resientialClass : Class;
[Embed(source="assets/Commercial-48x48.png")]
public commercialClass : Class;
[Embed(source="assets/Question-48x48.png")]
public questionClass : Class;
private function myiconfunction(data:Object):Object
var type:String;
var projectType:String = (data != null) ? data.project_Type : "QUESTION";
if (projectType == "RESIDENTIAL") {
return residentialClass;
}
else if (projectType == "COMMERCIAL") {
return commercialClass;
}
return questionClass;
}
</fx:Script>
Specify the renderer function the same way you do in your original code.
Extend the IconItemRenderer class
package {
import spark.components.IconItemRenderer;
public class CustomIconListItemRenderer extends IconItemRenderer
{
[Embed(source="assets/House-48x48.png")]
public resientialClass : Class;
[Embed(source="assets/Commercial-48x48.png")]
public commercialClass : Class;
[Embed(source="assets/Question-48x48.png")]
public questionClass : Class;
public function CustomIconListItemRenderer()
{
super();
iconFunction = iconFunc;
}
private function iconFunc(data:Object):Object
var type:String;
var projectType:String = (data != null) ? data.project_Type : "QUESTION";
if (projectType == "RESIDENTIAL") {
return residentialClass;
}
else if (projectType == "COMMERCIAL") {
return commercialClass;
}
return questionClass;
}
}
}
So then in your list you can assign it as itemRenderer="renderers.CustomIconListItemRenderer"

Flex 3: Strange behavior when function does a simple math operation

So, in one of my modules, a variable named numDays is created by looping through some XML data and finding the maximum value of an xml field calls days. After the numDays variable is found, I use it to find the width of a canvas by doing:
wrapper.width = numDays * parentApplication.oneDay;
(oneDay is a value determined by dividing the width of the window by 14)
So, now that I know how wide the wrapper canvas is, I can start to fill it. I have four items that are going into the wrapper: 1) Role description, 2) left controller, 3) name textbox (auto suggest component), 4) right controller. The width of the role description is 30px. The left and right controllers are 20 px each. So in order to get the width of the name textbox, I have a function that does the following:
nameTextbox.width = wrapper.width - 70;
for some reason when I do this, the application doesn't load fully. It pretty much stalls out. I have 30 "projects" with 15 "positions" within each of them. the wrapper.width is describing the width of a position, so there are 450 name textboxes trying to be figured out. Is this why it's messing up?
EDIT
So, from the main application, the project module is called. From within the project module, a list is created with a datasource of "positionsAC":
<mx:List id="wholePosition" dataProvider="{positionsAC}" width="100%" height="100%" paddingBottom="0" paddingLeft="0" paddingRight="0" paddingTop="0" backgroundAlpha="0" verticalScrollPolicy="off" itemRenderer="modules.position" useRollOver="false" selectable="false">
Below is a c/p of the position module in it's entirety
<?xml version="1.0" encoding="utf-8"?>
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:components="com.hillelcoren.components.*" dataChange="allData = data as Array" layout="absolute" creationComplete="init();" horizontalScrollPolicy="off">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
[Bindable] public var positionInfo:Array;
[Bindable] public var wholePositionID:Number;
[Bindable] public var allData:Array;
[Bindable] public var positions:XML;
[Bindable] public var startOffset:Number;
[Bindable] public var numDays:Number;
[Bindable] public var role:String;
[Bindable] public var studentID:Number;
[Bindable] public var conflict:Number;
[Bindable] public var studentType:String;
[Bindable] public var showInPrinciple:Number;
[Bindable] public var positionX:Number = 0;
[Bindable] public var positionWidth:Number = 0;
[Bindable] public var sName:String = new String;
[Bindable] public var asAC:ArrayCollection = new ArrayCollection;
[Bindable] public var conflictBG:uint = 0xffffff;
[Bindable] public var roleColor:uint = 0x000000;
private function init():void
{
if (allData)
{
getInfo(allData);
setBGColor();
getPositionX();
getPositionWidth();
getRightStudents();
}
}
private function getInfo(a:Array):void
{
var tempArray:Array = new Array;
startOffset = Number(a[0]);
numDays = Number(a[1]);
positionWidth = parentApplication.oneDay * numDays;
setStudentNameW();
role = a[2];
studentID = Number(a[3]);
tempArray = parentApplication.studentsDBIDDict[String(studentID)] as Array;
conflict = Number(a[4]);
studentType = a[5];
sName = "test";
showInPrinciple = a[6];
}
private function setStudentNameW():void
{
if (numDays == 1)
{
studentName.width = 55;
studentName.x = 37;
contractLeft.visible = false;
contractRight.visible = false;
}
else if (numDays == 2)
{
Alert.show("HI");
studentName.width = 133;
}
else if (numDays == 3)
studentName.width = 230;
else if (numDays == 4)
studentName.width = 330;
if (numDays > 1)
{
studentName.x = 47;
contractLeft.visible = true;
contractRight.visible = true;
}
}
public function setBGColor():void
{
if (conflict == 1)
conflictBG = parentApplication.errorColor;
else if (conflict == 2)
conflictBG = parentApplication.okErrorColor;
else
conflictBG = 0xFFFFFF;
}
private function getPositionX():void
{
positionX = parentApplication.oneDay * startOffset - 1;
}
private function getPositionWidth():void
{
positionWidth = parentApplication.oneDay * numDays;
}
private function getRightStudents():void
{
if (studentType == "freshman")
makeASAC(parentApplication.freshmanAC);
else if (studentType == "bfa1")
makeASAC(parentApplication.bfa1AC);
else if (studentType == "bfa2")
makeASAC(parentApplication.bfa2AC);
else if (studentType == "bfa3")
makeASAC(parentApplication.bfa3AC);
else if (studentType == "mfa1")
makeASAC(parentApplication.mfa1AC);
else if (studentType == "mfa2")
makeASAC(parentApplication.mfa2AC);
else if (studentType == "mfa3")
makeASAC(parentApplication.mfa3AC);
else if (studentType == "mfaw1")
makeASAC(parentApplication.mfaw1AC);
else if (studentType == "mfaw2")
makeASAC(parentApplication.mfaw2AC);
else if (studentType == "mfaw3")
makeASAC(parentApplication.mfaw3AC);
}
private function makeASAC(students:ArrayCollection):void
{
for (var i:int = 0; i < students.length; i++)
asAC.addItem(parentApplication.getStudentName(students.getItemAt(i)));
}
private function posLength(whichSide:String, expandContract:String):void
{
if (whichSide == 'l')
{
if (expandContract == 'e')
{
if (startOffset > 0)
{
numDays++;
startOffset--;
getPositionX();
getPositionWidth();
}
}
else if (expandContract == 'c')
{
if (numDays > 1)
{
numDays--;
startOffset++;
getPositionX();
getPositionWidth();
}
}
}
else if (whichSide == 'r')
{
if (expandContract == 'e')
{
if (numDays + startOffset < parentDocument.projectLength)
{
numDays++;
getPositionX();
getPositionWidth();
}
}
else if (expandContract == 'c')
{
if (numDays > 1)
{
numDays--;
getPositionX();
getPositionWidth();
}
}
}
//parentApplication.conflicts();
}
]]>
</mx:Script>
<mx:Canvas id="positionWrapper" width="{positionWidth}" height="25" backgroundColor="#ffffff" horizontalScrollPolicy="off" borderColor="#000000" borderStyle="solid" borderThickness="1">
<mx:Text id="roleText" text="{role}" width="25" y="3" color="{roleColor}" fontSize="11" fontWeight="bold" click="parentApplication.getDictLen(parentApplication.studentsDBIDDict)" />
<mx:Canvas id="leftSide" x="25" width="22" height="100%" mouseOver="expandLeft.visible = true; contractLeft.visible = true;" mouseOut="expandLeft.visible = false; contractLeft.visible = false;" backgroundColor="{conflictBG}" horizontalScrollPolicy="off">
<mx:Image id="expandLeft" source="images/addRed.png" y="5" click="posLength('l', 'e')" mouseOver="parentApplication.switchCursor(true);" mouseOut="parentApplication.switchCursor(false);" visible="false" />
<mx:Image id="contractLeft" source="images/minusRed.png" x="10" y="5" click="posLength('l', 'c')" mouseOver="parentApplication.switchCursor(true);" mouseOut="parentApplication.switchCursor(false);" visible="false" />
</mx:Canvas>
<components:AutoComplete id="studentName" textAlign="center"
dataProvider="{asAC}"
x="47" y="3"
/>
<mx:Image id="showSNW" source="images/searchicon.png" x="{(studentName.width + studentName.x) - 12}" y="5" visible="false" mouseOver="parentApplication.switchCursor(true); studentName.enabled = false;" mouseOut="parentApplication.switchCursor(false); studentName.enabled = true; showSNW.visible = false;" />
<mx:Canvas id="rightSide" x="{positionWrapper.width - 22}" width="20" height="100%" mouseOver="expandRight.visible = true; contractRight.visible = true;" mouseOut="expandRight.visible = false; contractRight.visible = false;" backgroundColor="{conflictBG}" horizontalScrollPolicy="off">
<mx:Image id="contractRight" source="images/minusRed.png" y="5" click="posLength('r', 'c')" visible="false" mouseOver="parentApplication.switchCursor(true);" mouseOut="parentApplication.switchCursor(false);" />
<mx:Image id="expandRight" source="images/addRed.png" x="10" y="5" click="posLength('r', 'e')" visible="false" mouseOver="parentApplication.switchCursor(true);" mouseOut="parentApplication.switchCursor(false);" />
</mx:Canvas>
</mx:Canvas>
Best to get a debugger. Flash Builder 4 is a pretty decent one, and they even give out free standard licenses to
Students, faculty and staff of eligible educational institutions
Software developers who are affected by the current economic condition and are currently unemployed
Event attendees who receive a special promotional code at their event
Check this out
Also, get debugger versions of the latest Flash Player here
The free standard might take some time to come, but you can always use the free trial in the meanwhile.
When you debug, check that nameTextBox is not null.

Resources