Max’s Little Brother
Read the entire book for free here: MaxsLittleBrother.com/book
Read the entire book for free here: MaxsLittleBrother.com/book
.
.
.
And how about that for a change of direction!;)
1 commentInfo about nwebb.co.uk
Hi,
unfortunately this site is no longer being updated as I am no longer developing Flex applications.
Thank you to those who have followed me and commented over the years (over 2500 of you!) it was fantastic to be a part of such a warm and wonderful community of developers.
Thanks again,
Neil.
No commentsFlashBuilder Hangs On Startup (Windows)
FlashBuilder was constantly ‘hanging’ when I attempted to start it up this morning. I tried killing the process and eventually rebooting, but neither helped – what did work was this solution (I’m blogging this because the post says you should find the relevant files in your ‘Adobe Flash Builder’ folder, whereas for me they were in my workspace folder. Here’s the link:
http://propagandaoculta.blogspot.com/2011/06/flash-builder-hangs-while-loading.html
No commentsASDoc and external libraries
The Problem
I’m generating documentation for my Flex project using ASDoc & I’m using an ANT build file to do so (the build file & properties file I’m using are based on the ones found here). When I tried to generate the documentation for this new project I got a bunch of errors. This is because the new project is using a 3rd party swc file (in my case, Robotlegs), so there is a Robotlegs .swc file in my libs folder. There were two issues:
1) ASDoc couldn’t find the RobotLegs classes that my classes were extending and so refused to compile any docs.
2) I don’t want the RobotLegs classes to be documented in my project documentation anyway!
The Solution
I found that a lot of people were running in to the same issue and struggling to find a solution that worked. This solution works if your third party library is a compiled swc file.
I added the directive -library-path+=${LIBS_DIR} to the compiler arguments (but I did this in a slightly different format … )
- If you look at the earlier link to the Adobe Devnet tutorial on which I based my ANT build/properties files, you can see I’m declaring LIBS_DIR inside the properties file, but if I wasn’t doing that I would have typed something more like this: -library-path+=${basedir}/libs
- I’m not using the command line directly either (I’m using an ANT XML file) so I actually added it in like this: <arg line=’-library-path+=${LIBS_DIR}’ />
My markup block for the ASDoc creation looked like this (see the above tutorial link if you need help, but basically I’m just showing roughly where to put the new markup if you’re using an ANT build file. If not, just add the compiler argument as shown above):
<!– ASDoc creation –>
<target name=”build doc” depends=”init”>
<exec executable=”${asdoc.exe}” failonerror=”true”>
<arg line=’-library-path+=${LIBS_DIR}’ />
<arg line=’-doc-sources ${SRC_DIR}’ />
<arg line=’-warn-level-not-supported=false’/>
<arg line=’-main-title ${DOC_TITLE}’ />
<arg line=’-window-title ${DOC_TITLE}’ />
<arg line=’-footer ${DOC_FOOTER}’ />
<arg line=’-output ${DOC_DIR}’ />
</exec>
</target>
By including the library path in the arguments it appears that your 3rd party swcs won’ t get documented (clear as mud right!), and when you run the ANT task, your project documentation should get generated without any issues.
I found this thread useful (thanks Jason!)
NOTE:
I had initally tried adding the -exclude-classes directive instead, but that gave me an error saying that -exclude-classes cannot be used alongside -doc-sources – clear as mud, again! (it has to be said that the Adobe documentation for ASDoc is really quite shoddy – ah the irony!)
[PHP] Basic Debugging Setup (PDT, Zend Debugger & WAMP)
Update
Since writing the post below I have swapped over to NetBeans 7 (PHP version) and XDebug … they are also free, plus it only took me 10 mins to set everything up, so read on :)
I continued to experience issues with the Zend set up – both with IDE features and ZendDebugger; for example, I was not always seeing changes that had been made to my code (as if it a cached version was sometimes being run) and I was not able to set cookies/sessions when running the app via the IDE. I expect these issues could have been resolved, but the setup:coding ratio I was experiencing was unsatisfactory & I found myself screaming “I just want to code!”
Setup of NetBeans & XDebug took less than 10 minutes including finding and installing the install files, and I’ve yet to find any major issues, even though Netbeans 7 is only in beta (on the couple of occassions something minor hasn’t worked as expected, the excellent built in bug-reporting feature has prompted me to add more details & submit the issue - Zend take note).
My current setup is:
- NetBeans 7 PHP version (beta) . That particular url may not be active for long, so also try http://netbeans.org/index.html
- XDebug – This link takes you to an excellent script that will automatically determine the version of the dll that you need to download, taking away all the hassle & headache (Zend take note again!)
- WAMP – I normally use XAMPP but, as the post below describes, I installed WAMP to solve an issue with ZendDebugger & decided to stick with it.
Once NetBeans & XDebug were installed I just needed to amend php.ini and restart the server. Because I’m using WAMP, I used this post & simply copied what had been posted by dcizal (thanks dcizal!) If your setup differs you should easily be able to find what you need on the NetBeans or XDebug forums.
So, it turns out that finding a decent PHP IDE with debugging, and setting it up doesn’t have to reduce you to tears. Who would have thought it?! ;)
… here is the original post:
Basic Debugging Setup
I wanted to write a quick PHP related post to help ease the pain for Flex developers, setting up an IDE which has the ability to do ‘site-wide’ debugging for PHP projects. This isn’t a “from-scratch” type post. I only really want to cover some of the things that may trip you up if you are a front-end developer and this kind of thing isn’t your strong point.
I’ll write it with this great guide in mind (the guide only concerns itself with setting up the ‘site-wide’ debugger part, so I’ll fill in some of the blanks) :)
Site-Wide Debugging?
Okay, I called it ‘site-wide’, what I mean by that is that you can debug your entire PHP website/app as a whole. It’s actually referred to as ‘remote debugging’ (as your site is running on a server, even if it is localhost) but I wanted to make the distinction because for us Flex developers, application-wide debugging is just ‘debugging’ (we also have ‘remote-debugging’). Anyhoo … the PHP debugger we’re going to use lets you debug a single PHP page, or, debug the site/app as a whole … for which you need to implement remote debugging. Got it?
Stuff To Get
A common free setup seems to be Eclipse with the PDT plugin, and Zend Debugger for debugging, so that’s what I’m using.
The company Zend have made a free contribution to the PHP community, which is Eclipse already bundled with PDT and Zend Debugger – useful!
Also, I’m going to use an all-in-one installer for PHP, MySQL and Apache, and once we’ve done all that, all that’s left to do is to download the files required to enable remote debugging! Go get all this stuff:
- PDT (Zend Build) (All in one IDE based on Eclipse – it comes bundles with the client-side Zend debugger – hurrah!)
- Studio Web Debugger (You’ll need this for remote debugging)
- WAMPP (if you already have PHP, MySQL, Apache set up & didn’t use WAMPP, make sure you read the note below)
Important Note For Non-WAMPP Users
In order to get remote debugging working we are going to have to place a .dll file on to our server (i.e. on localhost). The version of this file MUST match the version of PHP we are using. We must also decide whether to use the thread-safe or non-thread-safe (nts) version of the .dll – the guide I linked to above explains how to determine this, but don’t worry about it just yet.
So what does this have to do with WAMPP? Well, at the time of writing, all of the all-in-one PHP/APACHE.MYSQL installers I have found come bundled with PHP version 5.3, yet the debug .dll file version only goes up to 5.2 and I can testify that if you mismatch the versions, things will simply not work and you’ll get no feedback as to why ;)
I chose to use WAMPP as the way to install PHP, Apache & MySQL because it has a cool feature. WAMPP lets you change the version of PHP you’re using very easily via its control panel. This proved invaluable to me as I was able to downgrade to version 5.2 quickly. This feature may be invaluable to you too if there is a version mismatch when you’re trying to get things working. Of course you could manually (re)install PHP and do all the setup yourself but do you want to go through all that for a simple dev environment? ;)
Installing The Stuff You Got
Install WAMPP and check apache is running by accessing localhost. I won’t go through that in detail, as you can find explanations all over the web, and it’s very straight forward.
Now unpack PDT. Anyone familiar with Eclipse will probably already know that you don’t need to install anything, you can just find and run the eclipse.exe and you’re away! I set my Eclipse workspace to be the document root folder of WAMPP (in my case C:\wamp\www ) so that all my projects are instantly runnable on the local server – I’m sure there is a better way (let me know!). I then set up a new project, created a PHP file for it and added some breakpoints to the file. It was all very straightforward after coming from a Flex environment.
You can debug a simple PHP file straight away as long as you only want to debug it as a single file. Just debug it as you would in Flex – be sure to choose “Debug as PHP Script” from the debug menu (the little bug icon).
Finally we need to get the remote debugging working. For this, see the guide. The guide is in-depth. Follow it closely. Some of the boxes in the guide are scrollable, so make sure you scroll down to see all the code you should be adding in to your php.ini file, and make sure you do things like restart apache after you’ve made the changes.
I’ve covered most of the potential error points in this post already, but I’ll recap – these will make more sense once you’ve read the guide:
- Make sure that your version of PHP matches the version of the ZendDebugger.dll that you place on the server (ie. if you are using PHP: 5.2.4 then use the debugger .dll from the 5.2.x folder). If there is a version mismatch then use the WAMPP control panel … or whatever other means available … to downgrade the version of PHP you are using.
- Do NOT use the ZendDebugger.dll from a folder containing the letters nts (i.e. 5_2_x_nts_comp) unless your PHP installation is non-thread-safe (see guide)
- Re-read each step carefully – for example, don’t go adding
zend_extensionto your php.ini file when you meant to addzend_extension_ts - Check your phpinfo() page when you think you’ve got the dll sorted – it should be showing that debugging is enabled . Get this part working before you start worrying that any problem you have may lie with the IDE (see guide for instructions on the message that should be showing).
- If you do run in to problems: check that you can access localhost (127.0.0.1) from your browser , make sure you can ping 127.0.0.1 , make sure you have restarted Apache after making changes to the php.ini file, make sure you have changed the **correct** ini file and look on the web as there is a lot of help out there :)
Okay, so if everything is working properly, you should now be able to choose ‘Debug As PHP Web Page’ from the debug menu and your breakpoints should get hit, rather than being ignored.
The post is a bit rushed I know, but I wanted to get down the salient points for those of you that may be stuck. I hope it helps :)
1 commentAdding DataGrid Columns Programmitically
There may be reasons you want to create your DataGrid column(s) programmatically. If like me it’s something you only do now and again, it helps to have a little reminder of how to do certain things like applying a custom renderer, or ensuring that tabbing works correctly between your DataGrid’s child items.
The example code below should help with just that. It’s not a full explanation but should be enough to get you up and running quickly. See the notes for more detailed explanations of what some of the lines are doing.
Creating a Column
[as]
/**
* @return A ‘percent’ column for a DataGrid.
*/
private function createPercentColumn():DataGridColumn
{
var col:DataGridColumn = new DataGridColumn();
col.headerText = “Percent”;
col.setStyle(“textAlign”, “center”);
col.dataField = “percentage”; //See Note1.
col.editorDataField = “value”; //See Note2.
col.width = 100;
col.rendererIsEditor = true;
col.editable = true; //See Note3.
col.labelFunction = onValueFunc;
//See Note4.
var colItemRenderer:ClassFactory = new ClassFactory();
colItemRenderer.generator = ValueStepper;
colItemRenderer.properties = { totalToValidateAgainst:100, maximum:100,
investmentWeightingType:_dataPortfolio.investmentWeightingType };
col.itemRenderer = colItemRenderer;
return col;
}
[/as]
Note1: Specify the name of the property from your dataProvider that you want to display.
Note2: We are using a class called “ValueStepper” as the editor/renderer for our column. “ValueStepper” is a custom renderer I created which extends from NumericStepper (it is irrelevant what additional functionality ValueStepper has – all you need to know is that it is a type of NumericStepper) so we must use the NumericStepper.value property as the main property for our column. By default the DataGrid assumes columns use text and therefore will try to monitor a property called ‘text’ but because a NumericStepper doesn’t have a ‘text’ property we have to specify the property it should be using instead.
Note3: If you’ve set col.rendererIsEditor=true it may appear to make absolutely no difference whether you also set col.editable to true or false because you can input values regardless. However, setting col.editable=false will prevent your DataGrid from tabbing between the child items. You’re usually going to want to let your users tab through the input fields of each item in your DataGrid, so normally you will want to set this to true.
In order to allow tabbing of your DataGrid’s child items, it appears that you must also *explicitly* set dataGrid.editable=true on the DataGrid. Technically this means you don’t have to set the column’s editable properly UNLESS you do NOT wish to be editable (i.e. dataGrid.editable=false).
Note4: This is how you set a renderer for your column when creating the column programmatically. You need to create a ClassFactory and then set generator. You can pass properties in to your renderer as properties of an anonymous object (see “{}”). Finally you assign the ClassFactory instance to the col.itemRenderer property.
Calling The Column Method
Now we have a method to generate a column, here is a quick example of how you might call the above method:
[as]
dgColumns = new Array(); //dgColumns should be a bindable property
dgColumns.push(createPercentColumn());
[/as]
Instantiating Your DataGrid
Finally, here is an example of what your DataGrid MXML may look like. Note that I set the columns by binding to ‘dgColumns’. Also note that ‘editable’ is set to true, and I have disabled tabbing on the DataGrid instance, but allowed it for the children using ‘tabEnabled’ and ‘tabChildren’:
[as]
[/as]
Screencast – Flex3 Panel & List Message Component (Sizing)
If you’re having sizing issues with Panel or List, or if you just want a smooth scrolling List, this video may be worth a quick watch.
This solution hasn’t been tested to the nth degree but appears to work regardless of what I’ve chucked at it so far, so mainly for my own memory-jogging purposes I put together a quick screencast, and as usual I’m sharing it in case it proves useful to anyone else … because that’s the kind of sharing caring person I am :] Here it is:
http://nwebb.co.uk/videotutorials/messageWindowComponent/
A few background details:
I’ve been fighting Flex today. All I had to do was knock up a message window that could display any number of messages (of varying length). No big deal, done it before (to be honest I should have built a specific component by now) … anyway … maybe it’s because I’ve had time away from Flex, but for some reason this time I ran in to some really niggling sizing issues.
I’m using a List within a Panel (and the List is wrapped in a Canvas so I can achieve a smooth scrolling effect). The whole thing is being shown as a modal popup window.
For some reason I was finding that some sizes being reported were incorrect – I even had one particularly weird issue where the size of my List items were being measured as incorrect (too large) if I checked it using callLater, rather than when my dataProvider was set, but I simply couldn’t work out why. I came to a near solution but then found that scrolling wasn’t always trigging when I expected it to ( ‘callLater’ and various invalidation calls didn’t fix the issues either). The solution is simple, but as always, with various measuring options at your hands, it’s sometimes worth making a note of what’s worked for you – so that’s what I’ve done.
No commentsQuick Reference: Method Closures From The Ground Up
Although for the most part I understood what method closures were all about, if you had asked me to define exactly what one was, I previously would have stumbled, so I thought I’d solidify my knowledge a little by writing a quick post – I’ll keep it as short as possible!
Functions vs Methods
Just to make sure we’re all on the same page, you should know the difference between a function and a method: at its most basic, a function is a block of code that you can execute, and a method is just a function which belongs to an object. So, if I called ball.bounce() I would be calling the bounce method of the ball instance (bounce is a function, and is also a method of the ball instance).
Named vs Anonymous Functions
We can have named functions & anonymous functions. Those of you who have experience with AS2 may be more familiar with anonymous functions than those who don’t.
[as]
//named function (it has the name ‘sayHello’)
function sayHello():void{
trace(“Hello!”);
}
//anonymous function (function without a name)
function():void{
trace(“Hello!”);
}
//Example of using an anonymous function, AS2 stylie!
//The anon function is only called when the user clicks button1
button1.onClick = function(){
trace(“button was clicked”);
}
[/as]
‘this’ keyword
If we’re going to talk about method closures and scope, we should discuss ‘this’. Even though it won’t apply to my final example, it is important to understand.
Because I want to keep this post simple, I will just talk about how it behaves in AS3. What ‘this’ refers to, differs, depending upon whether or not you are using an anonymous or named function.
In AS3 if you use a named function, ‘this’ will always refer to the original context of the function definition (ie to the object on which you initially defined that function). Therefore, if you defined your named function on the main timeline of a Flash movie, ‘this’ would always refer to the root of that movie, and if you defined your named function within an instance of a class, ‘this’ would refer to that class instance.
[as]
//All of this code sits inside an instance of a class named ‘myBall’ …
function sayHello():void{
trace(this + ‘ says hello’);
}
var obj1:Object = new Object();
obj1.sayHello = sayHello;
var obj2:Object = new Object();
obj2.sayHello = sayHello;
obj1.sayHello();//will trace “myBall says hello”
obj2.sayHello();//will trace “myBall says hello”
[/as]
Anonymous functions act slightly differently. In this case it is ‘the object from which the function is being called’ that is represented by ‘this’. That may sound confusing so let me show you an example:
[as]
var obj1:Object = new Object();
obj1.sayHello = function(){
trace(this + ‘ says hello’);
}
var obj2:Object = new Object();
obj2.sayHello = obj1.sayHello; //note what I’m doing here!
obj1.sayHello();//in theory this should trace “obj1 says hello”
obj2.sayHello();//in theory this should trace “obj2 says hello”
[/as]
The code above says “in theory this should trace …”
Both calls would actually trace out [Object object] says hello because we are just creating instance of an Object. That’s not very useful. If we wanted to get a better trace we could give each object a ‘name’ property:
obj1.name = “obj1″;
obj2.name = “obj2″;
then we would change our anonymous function to say:
trace(this.name + ‘ says hello’);
What is important to note is that in the second instance, even though I assign obj1.sayHello to obj2.sayHello, ‘this’ still refers to obj2!
Now let’s define a couple of things and finish off by giving one last example which should illustrate the concept of method closures.
Lexical Environment (definition)
Big phrase, simple meaning: The lexical environment includes all the variables, properties, methods, and objects in the function’s scope chain, along with their values.
Why did you need to know that? Because it simplifies the definition of what a function closure is …
Function Closure (definition)
A function closure is an object that contains a snapshot of a function and its lexical environment.
Function closures retain the scope in which they were defined (remember what we said earlier when discussing ‘this’?). Hopefully the final example should solidify your understanding.
Example of a Function Closure
We’re going to create a named function which returns an anonymous function. The point of this is to show that the anonymous function has access to any arguments (and/or local variables) which were defined (or sent in to) in the named function. The anonymous function still has access to these arguments/variables even after the named function has finished executing! We say it ‘runs in the scope of the named function’.
[as]
/**
* The body of the inner function (anonymous) is not executed at the same time as
* the outer function (‘generator’). However, the inner function has access to the
* outer function’s argument (num1), because it is shares the same
*scope as the outer function.
*/
public function generator(num1:Number):Function
{
return function(num2:Number):Number //function closure defined
{
return num1 + num2;
};
}
//called when Flex’s creationComplete event fires…
public function onCreationComplete():void
{
//create a new function which will accept an argument
//& always add 10 to that argument
var addTen:Function = generator(10);
trace( addTen(5) ); //traces 15
}
[/as]
Okay, so the first time you look at this example it’s fair enough to think “Erm that’s confusing!” I’m sure you wouldn’t be alone in that, so let’s just step through it and I’ll try to explain what’s going on …
Whenever we call ‘generator’ we need to send in a number (‘generator’ is a named function that accepts an argument). ‘Generator’ will create and return an anonymous function, and because the anonymous function is created in the scope of ‘generator’ this anonymous function will have access to the argument we sent in. It continues to have access to it, even after ‘generator’ has finished executing!
So, when do we do this? In the above example we do it upon creationComplete: we create a function called ‘addTen’ (we call ‘generator’, sending in the number 10), and that returns our anonymous function, which, remember, has access to the number 10.
var addTen:Function = generator(10);
This number (10) is the num1 that we refer to in our ‘generator’ function.
When we eventually run ‘addTen’, it expects a parameter (this is the num2 that we refer to in our ‘generator’ function). We send in the parameter, which can be any number (here we send in 5), and it adds that number to 10.
… in other words, our anonymous function ‘remembers’ the number 10, even though the ‘generator’ function into which we sent 10 has already returned.
So What Is A Closure Again?
A closure is formed when an inner function is made accessible outside of the function in which it was contained. It still has access to the arguments and local variables of its outer function, and can be executed after the outer function has returned.
Flash Debug Player Causing Firefox Crashes – A Fix
Problem
If you’ve updated to a recent version of Firefox and also use the Flash Debug Player to debug Flash & Flex apps, you may already be painfully aware of the recent changes to the way Firefox 3.6.6 handles its plugins.
Now, rather than simply alerting you, letting you dismiss the error and continue on your merry way, Firefox takes control and kills the process for you because it detects that something is wrong. This completely disables the plugin and prevents you from debugging your app (while making you hang around for 45 seconds, just to infuriate you that little bit more) ;]
This also causes a problem during general day-to-day surfing because any site that doesn’t handle errors properly and/or throws null objects ( … yeah channel4.com I’m looking at you!) will also result in your Flash plugin being disabled.
Solution
You need to go in to the Firefox config settings (type about:config in to the location bar) and search for dom.ipc.plugins.enabled.npswf32.dll – double click that to set it to false. You may also need to set dom.ipc.plugins.timeoutSecs to -1. Now restart your browser and you should once again be able to debug your apps and dismiss the warnings as you used to do in the good old days. Ahhhhh bliss.
More info here.
[UPDATE]
In Firefox 4.0.1 dom.ipc.plugins.enabled.npswf32.dll (needed for the solution above) no longer seems available. You can still try disabling the plugin-crash-detection as follows: dom.ipc.plugins.timeoutSecs to -1
A Few Swiz Tips For Beginners
Swiz Beginner’s FAQ
When I started to look in to Swiz for the first time the other day, I had a few questions; Some answers were found in the official FAQ, and others elsewhere (mailing-group, web searches etc). I thought I’d collate what I found most useful, in to an easy to digest post that would make handy reading before starting your first Swiz project.
Remember that the docs are being updated all the time, and the alpha of Swiz 1.0 is already out, so check the official site for the most up-to-date info relating to the version of Swiz you are using. However I’d guess that most stuff here will remain fairly relevant for some time to come. I’m also likely to update this post when I come across/remember anything else, so check back.
Q & A:
Q: It looks as if I could end up with a lot of classes being instantiated in my BeanLoader(s). Wouldn’t that have a negative impact on start-up performance?
A: Look at using the Prototype class, which among other things offers deferred instantiation. Prototype beans will not be created until they are needed for injection.
Q:[Mediate] looks a lot like the equivalent of addEventListener(). Is there an equivalent of removeEventListener()?
A: “No. [Mediate] is generally used in non-view classes like controllers, so we didn’t account for that use case (weak listeners / removing mediate). Views should rarely be listening for ‘system events’ (and I think that is even more the case with views that are added and removed during app lifecycle*). Your views should generally just have a presentation model injected into them, or be managed by a mediator, or work in some other mostly passive fashion.”
From Ben Clinkinbeard via GoogleGroups archive.
* relates to the specific question being asked by the user.
NOTE: You can still use [Mediate] within a view. Take this example where the MainView class (which encompasses all the app’s views) listens for application-wide alerts and displays them via the Alert popup. This ‘listener’ does not need to be removed during the life of the application so it doesn’t seem to pose much of an issue (although there would still be better places to put this code, such as in the presentation model if you are using one).
//From Richard Lord's Swiz "Flexcast" example (http://www.richardlord.net/blog/flexcaster-swiz)
//This "listener" doesn't need to be removed during the life of the application.
[Mediate( event="AlertEvent.SHOW_ALERT" )]
public function alertEventHandler( event : AlertEvent ) : void
{
Alert.show( event.message, event.title );
}
Q: Where do I start implementing my logic?
A: Let your main controller implement the interface IInitializingBean and start your application logic in the initialize() method. Swiz calls initialize() on these beans when the autowiring process is complete.
e.g. public class ApplicationController extends AbstractController implements IInitializingBean
NOTE : Swiz autowires the view on addedToStage which is after creationComplete.
Q: I’ve looked at someone else’s Swiz project & I cannot see where [x] is being done. Am I going mad?
A: Possibly! … but it’s also possible to initialise a class in the BeanLoader and not refer to it elsewhere in the application, yet still have it react to mediated events. I’ve often seen things like an app’s main controller instantiated and used like this in demo applications. So, remember to check the BeanLoader for classes that are only referred to / initialised in that one place.
Q: I am injecting a property in to my view/presentation-model, but when I try to access that property it is still null. How can I be notified of when that property is set?
A: Instead of injecting in to a property, use a getter/setter instead. See the example below.
[as]
//THE FIRST WAY (but how do you know when ‘locations’ has been set?)
//[Bindable]
//[Autowire(bean="appModel", property="locations")]
//public var locations:ArrayCollection;
//THE ‘IMPROVED’ WAY ;)
[Bindable]
[Autowire(bean="appModel", property="locations")]
public function get locations():ArrayCollection
{
return _locations;
}
public function set locations(value:ArrayCollection):void
{
_locations = value;
//let other parts of my view react to that fact that locations is now set. For example …
dispatchEvent (new Event(“locationsLoadedFromDB”) ); //dispatching a custom event-type
}
//AND IN THE SAME CLASS ….
// The custom property dispatched from the setter will trigger this binding, and therefore anything bound to ‘nextBtnEnabled’ will update.
[Bindable("locationsLoadedFromDB")]
public function get nextBtnEnabled():Boolean
{
if(locations) return (locations.length > 0);
return false;
}
[/as]
Using ‘Proxy’ For Loading & Accessing Config Files
Configuration Files
Externalising some of your application’s settings in to config files is something most (hopefully all) of us do when writing Flex & AIR apps. It becomes almost essential when writing apps that may be moved around (e.g. say your place of work has a development server, a UAT/staging server and a production server – you don’t want to have to recompile your app each time you move from one server to the other … which is what you’d have to do if your application had things like URLs hardcoded in to them).
If you don’t yet use config files, consider how they would work given the above situation.
On our DEV server we’d have a file called ‘config.xml’, on our UAT server we’d have a file called ‘config.xml’, and on our PROD server we’d have a file called ‘config.xml’ too. Each file would contain settings specific to that particular environment. EG:
//IN DEV CONFIG <prop id="remotingEndpoint" value="http://localhost/weborb30/weborb.aspx"/> //IN PROD CONFIG <prop id="remotingEndpoint" value="http:/mycompanywebsite.com/services/weborb.aspx"/>
In our Flex app we would do the same thing regardless:
//IN OUR FLEX APP (not showing full tag here, but you get the idea)
<mx:RemoteObject
destination="GenericDestination"
endpoint="{appSettings.remotingEndpoint}"/>
The key point is that the Flex app doesn’t have to be recompiled when it is moved from server to server – it just picks up the local config settings for each environment.
A Common Approach To Creating/Loading Configs
One way people deal with config files is to create a class which loads in some settings file, usually as XML. This class most likely defines properties which correspond to the properties in the XML – the idea being that you populate the class properties from the XML properties. There are more efficient variations on this approach, but doing it that way is common – I was prompted to write this post after seeing someone doing just this. It has several drawbacks though. For example, each time you add a property to your XML you have to add that property (or getter method) to your class as well. You also end up having one class per config file, so it’s not particularly reusable.
The flash.utils.Proxy Approach To Creating/Loading Configs
What would be nice is if we had an easy way to load in our config XML file and have all the properties instantly available via our class. This is where flash.utils.Proxy comes in. There are four things we need to do in order to get this to work (five if we want our class to dispatch events as well):
- Make our class dynamic
- Extend flash.utils.Proxy
- Override its ‘getProperty’ method
- Use the flash_proxy namespace (in order to be able to override getProperty)
- (optional) Implement IEventDispatcher
I’ll go over the key points now but it’s easier to see it in action so I would recommend downloading this example file ( in FlexBuilder choose file->import->existing projects in to workspace … select ‘archive file’ and browse to the file you downloaded).
(1) Our class is declared using the dynamic keyword – essentially this allows other classes to request any properties from it, at runtime, and errors won’t be generated even if the property doesn’t exist. So we could ask for myinstance.thisVariableDoentExist and no error would be thrown.
(2) Next we extend flash.utils.Proxy because we want a way to capture these undefined property requests; With flash.utils.Proxy, when such a request occurs, a method of the Proxy class called getProperty() is automatically triggered for us (there are other methods triggered by other actions, so be sure to check out the docs – this is one handy class), and this allows us to do something in response to that request.
(3) We override the getProperty() method, so that we can write code which responds to the undefined requests.
(4) One small quirk is that the getProperty() method exists inside the flash_proxy namespace; All this means for us is that we have to import flash.utils.flash_proxy (intellisense won’t pick it up, but it does exist) and use it in front of the method (much in the same way we use access modifiers like ‘public‘ and ‘private‘) – see the code for an example.
(5) Our class also needs to load in some XML (our external config file), store it, and optionally dispatch an event once the data has loaded in, which is why I also implement the IEventDispatcher interface – if you’re not familiar with implementing IEventDispacher I suggest you look it up in the docs because about half the code in this class can essentially be ignored if you don’t want to dispatch events. We can’t extend multiple classes in AS3 – we are already extending Proxy, so we have to implement IEventDispatcher rather than extend EventDispatcher in order to get event dispatching functionality.
Putting It All Together
Okay, now that we can store XML and catch undefined property requests, we can see what property is being requested and look for it inside our loaded XML file. We return the value being requested, from the XML, but it appears as if the property belongs to our class. As the name implies, we are proxying the request on to the XML.
Below is a snippet of our class. You can see that in getProperty() I use e4x to grab the value out of the saved XML file. If the XML doesn’t contain the value then our method returns an empty String (of course you can modify this how you see fit):
package config
{
/**
* config.AppConfig.
*
* Loads & stores an XML config file, and returns the data from it as if the data belonged to the class instance.
* XML data nodes must be in the form:
*
(where nodeID & nodeValue represent your values).
*
* NOTE: Class is dynamic.
* Class overrides Proxy.
* Class imports & uses flash_proxy namespace.
*
*/
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.events.IOErrorEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.utils.Proxy;
import flash.utils.flash_proxy;
public dynamic class AppConfig extends Proxy implements IEventDispatcher
{
protected var _eventDispatcher:IEventDispatcher;
protected var _data:XML;
protected var _dataLoaded:Boolean = false;
private var _urlLoader:URLLoader;
public function AppConfig()
{
_eventDispatcher = new EventDispatcher();
}
public function loadConfigFile(url:String):void
{
var request:URLRequest = new URLRequest(url);
_urlLoader = new URLLoader();
_urlLoader.addEventListener(Event.COMPLETE, onDataLoaded);
_urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
_urlLoader.load(request);
}
protected function onDataLoaded(event:Event):void
{
_data = XML(_urlLoader.data);
_dataLoaded = true;
removeListeners();
dispatchEvent(new Event(Event.COMPLETE));
}
override flash_proxy function getProperty(name:*):*
{
if(_data)
{
var propName:String = name.toString();
return (_data.prop.(@id == propName).@value).toString();
}
}
//.....more class stuff not shown
So the upshot is that we now have a class that can load in any config file (as long as the XML follows the same formatting), and this class doesn’t have to be updated if/when the XML changes. We can instantiate n instances of our class and load in n types of XML file (one for config settings, one for strings, one for whatever else we need). It’s a handy way to approach the loading of config files.
9 commentsUsing WebOrb with AIR (Step 1)
[I'm using the .NET version of WebOrb but this should be generic enough to apply to all versions]
Examples Work For Flex But Not AIR
When you load WebOrb you get access to the ‘weborbconsole‘ which is a Flex app that runs on localhost). In the ‘Getting Started‘ section of this app there is a tutorial which will work if you run it as a Flex application, but will throw an error if you run it as an AIR application. I found some very helpful documentation pointing to the necessary changes, but it is slightly outdated so I thought I’d fill in the gaps.
Recommended Reading
First of all, here is the documentation which I’d highly recommend reading (I’ll summarise some of the key points below): Channels, endpoints and destinations
Config Files
Okay, so if you stuck with the default installation then you should find all your config files located here:
C:\inetpub\wwwroot\weborb30\WEB-INF\flex
The two files we are most interested in are services-config.xml (specifies channels) and remoting-config.xml (specifies destinations). The former references the latter.
In remoting-config.xml you can see that some default channels are defined:
<default-channels>
<channel ref="my-amf"/>
<channel ref="my-secure-amf">
</default-channels>
You can also see that a bunch of destinations are declared, one of which is called “GenericDestination” and uses a wildcard (*) for the source:
<destination id="GenericDestination">
<properties>
<source>*</source>
</properties>
</destination>
When the wildcard is used, WebOrb uses one of the default-channels. The default channels apply to all remoting destinations unless a channel is specifically referenced in a destination declaration (thanks Mark!).
You can see that the first of the default channels references the “my-amf” channel, so by default any [non-secure app?] setting the destination to be “GenericDestination” will actually end up using the “my-amf” channel. Here is an example of the Flex RemoteObject tag doing just that:
<mx:RemoteObject id="compinfo" destination="GenericDestination" source="noteperfect.remote.ComputerInfoService"
showBusyCursor="true"
fault="faultHandler(event)">
<mx:method name="getComputerInfo" result="getComputerInfoHandler(event)"/>
</mx:RemoteObject>
A Solution
The documentation I linked to ( above) explains how AIR needs absolute URLs rather than relative URLs, and this is why we need to use different channels for Flex and AIR apps ( the only difference being that the AIR channel has an absolute reference to the endpoint uri ) . The doc also refers to “my-air-amf” and “GenericAIRDestination“. I could only find an “air-http” channel definition in my version of WebOrb, and as for “GenericAIRDestination“, that does exist (in weborb-services-config.xml) but it is exactly the same as “GenericDestination“.
So, I was able to get the example working as an AIR app by doing the following:
- Changing the default channel in remoting-config.xml to be <channel ref=”air-http”/>
- Setting the destination on my RemoteObject in Flex to be “GenericDestination” (rather than GenericAIRDestination)
I’m brand-new to WebOrb and I’m not suggesting that this is the only/best/recommended way to get things working – although the documentation seems to suggest it is. Please feel free to leave a comment if there is a better way.
——————————————————————————————-
Edit:
After chatting to Mark from MidnightCoders I’ve changed things slightly. All I’ve really done is:
(1) Reverted the default-channel to be “my-amf” (in remoting-config)
(2) Copied “GenericAIRDestination” ( from weborb-services-config.xml) in to remoting-config, and added both a channels attribute and tag – see the xml below.
(3) Changed my RemoteObject in Flex to use “GenericAIRDestination”
In Remoting Config:
<default-channels>
<channel ref="my-amf"/>
</default-channels>
Also In Remoting Config (copied over from weborb-services-config & ammended):
<destination id="GenericAIRDestination" channels="air-http">
<channels>
<channel ref="air-http" />
</channels>
<properties>
<source>*</source>
</properties>
</destination>
In Services Config:
<channel-definition id="air-http" class="mx.messaging.channels.AMFChannel">
<endpoint uri="http://localhost:80/weborb30/weborb.aspx" class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
<polling-enabled>false</polling-enabled>
</properties>
</channel-definition>
In Flex App:
<mx:RemoteObject id="compinfo"
destination="GenericAIRDestination"
source="noteperfect.remote.ComputerInfoService"
showBusyCursor="true"
fault="faultHandler(event)">
<mx:method name="getComputerInfo" result="getComputerInfoHandler(event)"/>
</mx:RemoteObject>
I won’t go too much further in to this, because (a) Mark has indicated that things will probably be made easier in WebOrb 4, and (b) my next post is going to be about using WebOrb & AIR without referring to the config files at all ( courtesy of Dan from the moov2 team).
Thanks to Mark for all his help.
No commentsIf WebOrbConsole Doesn’t Load
Today I installed/enabled WebOrb for .NET & IIS7 on a Windows7 machine.
Initially the WebOrbConsole page ( which is a Flex app ) loaded without issue, but at some point after a reboot I revisited the console page and it was blank ( ie I saw nothing but the default blue Flex background; the console never loaded. I didn’t even see the preloader ).
I’m completely new to IIS. ,NET and WebOrb so after doing the obvious stuff (restarting the server etc) I found myself a little stuck. Thankfully Dan came to the rescue and got things working and I thought I’d relay what he did, as it was a little odd. We still don’t really know what was the cause of the issue. It didn’t seem to be the permissions, although that would seem a likely cause.
- Make a copy of the default weborb folder ( it will be named weborb30 or something similar, depending on the version of WebOrb you are using – the default location for me is C:\inetpub\wwwroot\weborb30 ).
- Rename it to whatever you want ( e.g. weborbdan )
- Bring up the IIS management console (on Win7 this is start->control panel->system & security->Administrative Tools->IIS Manager). It should look something like this:

- Right-click on “Default Web Site” and choose “Add Application…” You should see a window popup that looks like this:

- Give it an alias ( we used the same name as the folder i.e. weborbdan )
- Next, browse to the physical path of the folder. The popup should look something like this:

- Now open up a browser and navigate to C:\inetpub\wwwroot\[nameOfYourFolder] ( e.g. we would browse to C:\inetpub\wwwroot\weborbdan )
The strange thing was that we then copied the contents of weborbdan back in to weborb30, and that started working again, so something had obviously gone slightly awry.
Note: If you want to know the difference between an Application & a Virtual Directory look here or here
No commentsWebOrb & .NET (Security Sandbox Violation)
I’m guessing this is a real [.NET] newbie error but I wanted to record it anyway - for anyone searching for a potential cause/solution.
Today I installed WebOrb for .NET on Windows7 (IIS7).
I was given a .dll file which I put in my bin folder and I renamed the .dll (bad move! I was assured that this wouldn’t cause any issues but it did).
As you can see below, the error messages I received did not indicate the cause:
[SWF] /weborb30/NotePerfect_FLEX-debug/NotePerfect_FLEX.swf - 952,782 bytes after decompression Warning: Failed to load policy file from https://localhost/crossdomain.xml *** Security Sandbox Violation *** Connection to https://localhost/weborb30/NotePerfect_FLEX-debug/weborb.aspx halted - not permitted from http://localhost/weborb30/NotePerfect_FLEX-debug/NotePerfect_FLEX.swf
I had a cross-domain policy in place but still got this error, and I’m not sure why renaming the .dll causes Flex to try to connect over https. Anyway, re-renaming the file sorts the problem.
No commentsiPad
[I own shares in Apple, a MBP, an iPhone, a PC and I'm a Flash platform developer]
Is the iPad for Me? Most certainly not (no USB, no SDcard, no multitasking, no Flash, no camara, yet another data plan. Meh.)
Is the iPad for my mum? Nope. “Mother Webb” already has a laptop and she already has an established web-experience which involves Skype, iPlayer, various Flash-based video sites. She even runs Firefox and nod32 for goodness sake.
Is the iPad for someone too scared or baffled to buy a computer?
- Closed platform (closed = control = stability) which is what you want for that target audience. I simply don’t buy in to the “Apple are supporting the open-web by banning proprietary software” hype. Can you install Firefox, Chrome or another browser of your choice on the iPad? (http://bit.ly/aaysEp)
- No Flash = no full web experience. Well, if you’ve never had it you probably don’t know what you’re missing. Yes, you won’t have a full web experience as we know it, but if you’re someone too scared to buy a computer then you’ll just be happy that your overall experience is stable. You will probably want to access the mainstream sites like iPlayer and YouTube … but there will be apps for those.
- No multitasking – simple is good for you and you won’t be familiar with it anyway.
- No camera – to me this is the big mistake. This market will be very likely to want a device which allows them to video-chat with relatives … but it looks like the OS has support for it. Maybe version 2.
- You may have to pay for normally-free legal content eg “Instead of being able to watch the Daily Show & Colbert Report free with ads, my only option is to buy them on iTunes“ http://9to5mac.com/flash-on-iPad-23445456 - but again, if I’m right then we’re talking about a market that is not experienced with the web. They may almost need to be spoonfed for a while. What better than giving them a central place to get what they want? They’ll be happy to pay – partly because they won’t even realise they can get it for free.
I can see many, many arguments against what Apple have done, but I also see some arguments for it – don’t be fooled, I’m responsible for arguing the other side of the coin (ie against what the iPad represents) on various email discussion lists and blogs right now.
But also, let’s make no mistake, “people too scared/confused to buy a computer” represent a huge untapped market. I think this device, as-is, may almost be perfect for them. It is not perfect for me, so I won’t buy it. As simple as that. I’m not sure I want it to be successful for a variety of reasons, but I’m quite sure it will be. The travesty for me is that it could have been very suited to me. I feel cheated out of a new toy! The iPad is like fisher-price computing.
Apple, the techie in me feels very (VERY) uncomfortable with your closed platform, and I believe there are many valid arguments against what you’ve done, but I think I understand who you’re aiming at.
5 commentsCreating read-only bindable properties in Flex.
There may be times when you use an implicit getter/setter to take advantage of data-binding, but you’d prefer the property to remain read-only to the outside world.
When trying to do this many people initially attempt the following:
[Bindable]
public function get someValue():int
{
return _someValue;
}
private function set someValue(value:int):void //<-- 'private' access modifier
{
_someValue = value;
}
Note the use of the ‘private’ access modifier on the setter. Unfortunately this can lead to an “Ambiguous Reference” error at compile time (if you try to set the value in your code) and is not the correct way to approach the issue.
Other people attempt to create the getter method only (marking it as ‘bindable’), but don’t create the corresponding setter method. However, this results in error at compile time if you try to set the value (“Attempted access of inaccessible property“) or a warning at compile-time: “A bindable tag on a read-only getter is unnecessary and will be ignored“. Don’t bury your head in the sand and ignore this warning – your binding may initially appear to work because Flex retrieves all the values at application start-up, but subsequent changes to the value will not be reflected.
Recap – [Bindable] & What Happens Behind Scenes
Let’s quickly go over what is happening behind the scenes when we use the [Bindable] meta tag.
You may have seen the Bindable tag written several different ways:
- [Bindable] //default
- [Bindable (event="someEventType")] //specifying an event type
- [Bindable ("someEventType")] //short-hand way of specifying an event type
When we use [Bindable] with no event argument specified, certain code is generated for us behind the scenes at compile-time. Some of that code ensures that an event called “propertyChange” gets dispatched for us when our bindable value has been set. Although we just write [Bindable], Flex interprets that as [Bindable (event="propertyChange")] and once our value has been set, “propertyChange” (represented by the static constant PROPERTY_CHANGE on the PropertyChangeEvent class) is also dispatched for us.
Essentially this is what is happening:
[Bindable (event="propertyChange")]
public function get someValue():int
{
return _someValue;
}
public function set someValue(value:int):void
{
if(value != _someValue)
{
_someValue = value;
dispatchEvent(new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE));
}
}
It’s worth noting that the auto-generated code also performs a check, only updating the value if the value being set is different to the current value (see One Reason Why Your Flex Getter May Not Execute)
So, when you use [Bindable], Flex is guaranteeing that it will be responsible for dispatching the “propertyChange” event for you, and to do this it needs the corresponding setter method. If you don’t provide a setter, it doesn’t have a opportune point at which to dispatch the “propertyChange” event for you. It also throws up the warning “A bindable tag on a read-only getter is unnecessary and will be ignored“. This error seems to suggest that you may be wrong in wanting a private setter, but really it’s a poorly worded error-string and what it’s trying to say is that if you’re relying upon the default [Bindable] behaviour (ie where “propertyChange” being dispatched) you’re going to need a public setter method too.
Declaring An Event
As soon as we specifically declare an alternative event type, we break the contract with Flex , and in doing so become fully responsible for dispatching our own event. Flex doesn’t care where we dispatch the event from. It could be from inside a setter, but it doesn’t have to be. If we don’t dispatch any event at all Flex won’t complain – our code simply won’t update as expected. Let’s take a lok at specifying our own eventType and dispatching it ourselves:
private var _someValue:int;
[Bindable (event="someValueChange")]
public function get someValue():int
{
return _someValue;
}
private function generateRandomValue ():void
{
_someValue = Math.round(Math.random() * 100);
dispatchEvent(new Event("someValueChange"));
}
Although we only have an implicit getter method (no corresponding setter), we no-longer get a compiler warning. Our implicit getter looks like a public property, but because it has no setter it can’t be set. _someValue is private and so that can’t be set from outside the class either.
In order for binding to work, we need to send out a notification when the value changes, so that anything bound to it can call the getter and retrieve the updated value – we fulfil this obligation by associating our getter with “someValueChange”, and then dispatching an event with “someValueChanged” as our eventType when our value changes – the difference is, we’re doing this from a method in our code (in this case, a private method) and not from a corresponding setter.
Again, the thing you must remember is that, when you specify [Bindable(event=”") you *must* dispatch the event yourself, even if you are creating a public getter/setter. Whatsmore, it’s recommended to avoid the default for performance reasons – if multiple properties all dispatch “propertyChange” when their value changes, imagine the extra work your app has to do to work out which property-change caused the event to be dispatched.
Note: In the last example, I used flash.events.Event with an event type of “someValueChange” but we could have dispatched any subclass of Event (as long as the event type was “someValueChange”). As always, the examples shown are kept small and concise and not intended to indicate best practice.
10 commentsNew AIR Video Tutorials
I’ve just added a couple of new AIR tutorials to my video tutorials page:
1. Reading In A Text File (and displaying the icon)
2. Recursively searching a users hard-drive for files of a particular type
I’m still playing around with the recording format to see what works best (feel free to let me know). In the first video I code the application while you watch, and in the second video I review some existing code line by line.
These videos are part of my own AIR learning process so be aware that I may not necessarily be showing you the optimal way of doing something, but hopefully I am. Apologies for the audio, especially on the second recording – my new Skype headset obviously doesn’t make my muddy voice any clearer ;) I hope you can still understand what’s being said.
No commentsFlash vs HTML5 – a few points for HTML5 advocates to consider :)
For any developer worth their salt, it will ALWAYS be about using the right technology for the job. Because I’m primarily developing on the Flash-platform I’m looking for areas where I see the Flash-platform as being beneficial over other current technologies. I’m not a fan of all-flash websites, but I’ve found the platform perfect for creating a dynamic UK online examinations application, an internal application to generate visual interactive representations of text documents (try saying that when you’re drunk), and for creating enterprise-level financial applications.
Why The Flash vs HTML Post?
[ Edit - I'm surprised no one has picked me up on this yet, but something quite important that I forgot to add in here - the "Flash vs HTML" part of the title is a reference to the childish "my technology is better than your technology" style rants I've been reading, and not intended to suggest that it's an either/or situation - I'm sure that's pretty evident from the rest of this post but just thought I'd clarify it]
They say that “change is the only constant” and online boundaries are certainly shifting all the time; Some of the more traditional benefits that the Flash-platform has/had are lessening over time due to advances such as the HTML5 spec, but new ones are appearing too – for the record I’m all for HTML5; So many of the reasons I disliked HTML in the first place are being addressed and that is great news! Yet the Flash-platform does have benefits in certain areas; Just because they may not fit your particular use-case, it doesn’t mean they do not exist. There are so many short-sighted, one-sided arguments flying around on both sides of the Flash vs HTML5 debate that it’s hard to know where to start. People often make the assumption that there is only one use-case. They ignore real-world time & budget constraints. When criticising Flash they all too often rely upon extremely outdated arguments that no longer apply, or talk about how the web is too important to be in the hands of a single company (I agree – but companies do drive technology sometimes). It’s a shame because some arguments against Flash are very valid, but they lose credibility when paired with ones that are not.
I’ve no doubt that any Flash vs HTML5 post will cause some kind of angst-ridden spat amongst the party-faithful on each side, but I am eternally optimistic that there can be reasoned, grown up discussions without resorting to child-like attacks – let’s see if I’m right ;)
My reasons for this post? To encourage people to think reasonably, to dispel a few myths about Flash and to hope HTML5 users avoid mistakes that were made by us in the Flash world … “skip-intro” anyone :)
The Arguments
1. The “I hate Flash adverts” argument
I hate Flash ads too. For the most part I hate all ads (possibly with the exception of a few subtle targeted ones like Google Adwords). Adblock Plus is a faithful friend of mine. If you say that you hate Flash *because* of ads, remain balanced and make sure you start declaring your hatred for HTML5 as soon as some ad agency uses a Canvas element to deliver a nauseating advert to your page. Animated adverts with sound should become easily achievable in HTML5. Advertisers use Flash at the moment because it’s the best medium for them to deliver their content. When HTML5 allows them to deliver that same rich content, which medium do you think they’re going to choose? Yes you got it, HTML5! Blaming a technology for the way people misuse it is, in the main, pretty dumb. It’s just as wrong to blame HTML5 as it is to blame Flash. They are simply technologies which facilitate the delivery of rich media.
2. The “I hate Flash because it’s not fully accessible” argument
HTML5′s Canvas tag allows people to do some amazing stuff. I’ve seen some stunning examples already (side note: in the early days of Flash, examples were ripped off from Java, so it’s amusing to see many HTML5 examples apparently ported over from Flash). However, be warned, HTML5′s canvas tag is not accessible. I guess the question is how do you make such varied dynamic content accessible? Now that HTML5 can DO MORE, it will face more of the problems that the Flash-platform faced (because it could DO MORE). The more a technology can do, the more challenges it will face. Make sure that if you used the “I hate Flash because of the lack of accessibility” argument, that you avoid using the HTML5 Canvas tag altogether until the issue is sorted, if it ever is.
3. The “Flash video is rubbish & the iPhone shows we don’t need it anyway” argument
The iPhone delivers video without needing Flash so why can’t everyone else? First of all, as I mentioned above “change is the only constant”. Now that many of us have broadband we can get high quality video streamed to us, but that wasn’t always the case and believe it or not, still isn’t for many people in the world. Flash became popular because it could deliver streamed/progressive video better/faster than many alternative solutions around at the time. There are other use-cases besides watching YouTube. Video content creators may want to take advantage of overlaying, queue points, filters and other additional features which I’m not sure are offered by all the other formats. It’s a shame to see a technology get knocked because it did something so well. Flash video really did facilitate change on the web. I’m not saying don’t start investigating alternatives, but I think it’s a strange argument for hating Flash. I would rather hear people say something along the lines of “Our video requirements are evolving and we need to review our reliance upon Flash as a video streaming solution” … though I wouldn’t necessarily agree outright ;) … and finally, as Flash is already the establish default format is it necessarily right to force content providers to have to provide alternatives as well? I’ll leave that open ended as I’m sure there can be lots of good discussion around that point.
4. The “Why Doesn’t Adobe Use Open Standards?” argument.
This is a biggie and I’m obviously not equipped to answer it all. I know that ActionScript3 (the language behind Flash, Flex and AIR) was meant to be 100% ECMAScript compliant, but the ECMAScript committee decided to halt work on ECMAScript 4 in favour of an incremental updated to JavaScript (known as ECMAScript 3.1) and I guess Adobe couldn’t just hang around. You can read about that here. In Flex 4 there is also a declarative graphics language called FXG. Why didn’t Adobe use SVG? Adobe stuck as near to the conventions as they could but felt unable to go 100% with SVG. You can read all about it here. Standards take time to agree upon and still not everyone will agree – I’ve already seen HTML developers complaining about decisions being made in the HTML5 spec. I’m not suggesting Adobe are angels by any means, but I think the argument can be unfairly levelled at Adobe here and they seem to want to adopt or stay close to open standards where possible ( plus they’ve been open-sourcing a lot in recent years).
BONUS ARGUMENT: The “I hate Flash intros” argument
Don’t we all? HTML5 will allow people to do more, and because they can do more they almost certainly will! All I can say is you must learn from the mistakes of us Flash guys. Don’t give your users some crazy navigational controls just because you can – only do it if it benefits THEM! And for goodness sake, perform user testing where possible :)
In Conclusion…
There are really exciting times ahead. I’m genuinely excited by HTML5 but I’m under no illusion that there won’t be things to dislike too – What? You’re telling me it’s going to be perfect? ;) – but it will be interesting to see if those shortcomings escape as much hate as I see levelled at Flash in certain circles simply because HTML5 is an open standard. The thing is, any technology can be misused and the more powerful it is, the more the scope for misuse … with HTML5’s new tags such as Canvas I’m sure we will see a host of truly awful things. That is not a reason to hate HTML5 and I hope Flash developers will not resort to tit-for-tat arguments. If you can separate the technology from the (mis)use of the technology it gives you a far more balanced picture. Flash does have problems but I’m happy as long as they are constantly being addressed, and if you look at many of the announcements from Adobe this week, you can see that it is still evolving and issues *are* being addressed – working with ARM to make improvements at chip-level is just one new example.
Meanwhile if you want to check out some truely great Flash-platform apps I would recommend:
http://www.hobnox.com/audiotool
http://www.lovelycharts.com/
http://www.balsamiq.com
p.s. Sanity note for fellow iPhone/Apple guys – Apple lock down their app store, tie you into a single provider, they expect you to pay for an mp3 and then *pay again* to turn it in to a ring tone and they have historically made it difficult for people to get the music they (may have) purchased off of the iPod and on to another device – iPhone owners seem to be the worst critics of Flash, so just bear that in mind when you’re talking to me in the pub about how bad Adobe are & discussing openness … just thought I’d point that out ;)
p.p.s Flash may not be on the iPhone for many reasons. My personal thought: the main reason for exclusion is that it would break the entire app-store model and allow people to get apps for free. As an iPhone & MPB owner, and someone who owns shares in Apple, I see the different sides of the argument. I want my shares to increase in value and I’ve never been particularly desperate to see Flash fully supported on the iPhone … but I *would* at least like a cut down version which allows me to view any Flash video, and not just content from the major sites. Plenty of great home-made tutorials out there, only available in Flash!
33 commentsSeemingly Bizarre Type-Coercion Error
What The Flex?!
About an hour ago I started work on a new class and ended up with quite a strange error message which looked a little like this (the names have been changed to protect the innocent)
TypeError: Type Coercion failed: cannot convert Project.folder::MyClass@2d28301 to Project.folder.MyClass
Spot the oddity?
Cannot convert Project.folder.MyClass@2d28301 to Project.folder.MyClass. Hmmmm.
This one had me a little stumped.
The project I’m working on at the moment is modular and the only difference I could see was that this object was both created and added to a global collection from within a particular module, then I was attempting to retrieve it from the next module, whereas all the other data objects were instantiated in the application start up process, then retrieved from within various modules.
Solution
A quick post on FlexCoders got me the answer I was looking for. My object belonged to a different Application Domain than the rest, and therefore as far as Flex knew, these two classes were different.
The fix was to specify the ApplicationDomain for my module loader:
[as]
_mainModuleLoader = new NoviaModuleLoader(); //extends ModuleLoader
_mainModuleLoader.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
[/as]
Thanks very much to Ian Thomas on FlexCoders for providing the solution.
You may also be interested in reading this.
11 commentsFiltering an ArrayCollection On Multiple Properties/Values
In my application I have an ArrayCollection containing some objects. I only want to display certain subsets of these objects in certain view components, and I can achieve that by applying a filter function to the collection, but sometimes you may wish to filter on multiple properties or property values. First, let’s take a quick look at how the filter function works:
Basic Filtering
Each time the filter function gets called it receives an Object (an item from your ArrayCollection) and must return true or false – false if you want the object to be filtered out of the collection.
Let’s suppose we have a collection of objects with a name property, and each object looks a bit like this: {name:”someNameString”}. We may want to only show objects with the name “AAA”. Therefore our filter function may look like this:
[as]
collection.filterFunction = myFilterFunction; //set the function to use
collection.refresh();//must do this to cause anything using the collection to update.
private function myFilterFunction (item:Object):Boolean
{
return ( item['name'] == “AAA” );//returns true only if our ArrayCollection item ‘name’ property is “AAA”
}
[/as]
Filtering On More Than One Value
Now what if you want to filter on multiple properties or multiple property values? Say we wish to filter properties that have either the name value “AAA” or the name value “EEE”? We could create a filter function like this:
[as]
private function myFilterFunction1 (item:Object):Boolean
{
return ( item['name'] == “AAA” || item['name'] == “EEE”);
}
[/as]
… but what if we now wish to filter objects with the names “AAA” or “BBB”? Well we could create yet another function to do that, and what if we want to filter on values “BBB” and “CCC” ….. as you can probably tell, if we have multiple values and wish to filter on any combination of these values it’s going to lead to a combinatorial explosion of filter functions very quickly.
It would be better if we could send these values in as arguments to our filter function and have our function check against all of them, but sending in arguments doesn’t seem to be an option, so instead we can set a class level property on our main class which can hold all the values we wish to filter on.
Creating A Basic Filtering Class
Rather than do that, it’s best to encapsulate this functionality in to a separate class with the specific responsibility of taking care of our filtering. We can create a class with a property that can hold an array of filter values, and a function on that class which will generate our filter function for us too.
If that was as clear as mud, let’s say our new class is called FilterSelector and see how we would use it in our application:
[as]
//IN OUR MAIN CLASS…
var fs:FilterSelector = new FilterSelector();
fs.filterField = “name”; //filterField is a property we’ve created to hold the field we want to filter on
fs.addFilterTypes(“AAA”, “CCC”, “EEE”);//the values we want to filter on.
collection.filterFunction = fs.generateFilterFunction();//USE THE OBJECT TO GENERATE THE FILTER FUNCTION
collection.refresh();
[/as]
Inside the FilterSelector class, our generateFilterFunction() may look like this:
[as]
public function generateFilterFunction():Function
{
var f:Function = function(item:Object):Boolean
{
for each(var type:String in _filterTypes)
{
if(item[filterField] == type) return true;
}
return false;
};
return f;
}
[/as]
As you can see, the generateFilterFunction() function returns an object of type Function. What it actually does is to create an anonymous function (a function without a name), which will loop over all our filter types, and returns this anonymous function to us, which we then set as the filter function. This anonymous function will return true as soon as any match is found, else return false if there are no matches.
Extending The Class
There are lots of places you can go with this. It may also be the case that you want to filter on different properties (ie rather than “AAA”, “BBB”, you may wish to filter on “name”, “label”) in which case you could accept multiple values for the filterField property too, then set up a nested loop for both property & value, and cross check each one.
In reality you may actually need multiple filter functions, as they may be markedly different. You could create another class with a specific type (eg IFilterFunction) and set the function you wish to use as a property on the FilterSelector class – this could get tricky, depending on how complex and specific your filters are, or you could do something similar to what is described in these posts: here & here.
Apologies for this rushed post (and hence lack of embedded working Flex example). Here is a link to a working example, exported as a Flex Project Archive .
I’ve not looked at the filtering in Flex ArrayCollections before. If you think you have a better solution feel free to chime in :]
1 comment
