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!;)
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!;)
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]
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.
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.
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
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: 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).
[as3]
//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 );
}
[/as3]
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]
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:
[as3]
//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"/>
[/as3]
In our Flex app we would do the same thing regardless:
[as3]
//IN OUR FLEX APP (not showing full tag here, but you get the idea)
<mx:RemoteObject
destination="GenericDestination"
endpoint="{appSettings.remotingEndpoint}"/>
[/as3]
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):
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):
[as3]
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
[/as3]
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.
[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:
[as3]
<default-channels>
<channel ref="my-amf"/>
<channel ref="my-secure-amf">
</default-channels>
[/as3]
You can also see that a bunch of destinations are declared, one of which is called “GenericDestination” and uses a wildcard (*) for the source:
[as3]
<destination id="GenericDestination">
<properties>
<source>*</source>
</properties>
</destination>
[/as3]
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:
[as3]
<mx:RemoteObject id="compinfo" destination="GenericDestination" source="noteperfect.remote.ComputerInfoService"
showBusyCursor="true"
fault="faultHandler(event)">
<mx:method name="getComputerInfo" result="getComputerInfoHandler(event)"/>
</mx:RemoteObject>
[/as3]
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:
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:
[as3]
<default-channels>
<channel ref="my-amf"/>
</default-channels>
[/as3]
Also In Remoting Config (copied over from weborb-services-config & ammended):
[as3]
<destination id="GenericAIRDestination" channels="air-http">
<channels>
<channel ref="air-http" />
</channels>
<properties>
<source>*</source>
</properties>
</destination>
[/as3]
In Services Config:
[as3]
<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>
[/as3]
In Flex App:
[as3]
<mx:RemoteObject id="compinfo"
destination="GenericAIRDestination"
source="noteperfect.remote.ComputerInfoService"
showBusyCursor="true"
fault="faultHandler(event)">
<mx:method name="getComputerInfo" result="getComputerInfoHandler(event)"/>
</mx:RemoteObject>
[/as3]
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.
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.



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
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:
[as3]
[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
[/as3]
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.
[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?
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.
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:
[as3]
[Bindable]
public function get someValue():int
{
return _someValue;
}
private function set someValue(value:int):void //<– ‘private’ access modifier
{
_someValue = value;
}
[/as3]
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:
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:
[as3]
[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));
}
}
[/as3]
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:
[as3]
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"));
}
[/as3]
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.
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.
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!
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.
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 :]
Just wanted to write a quick post, mainly to serve as a reminder for myself :]
I was asked to generate some XML (in Flex) which returned
<nodeName xsi:nil=”true”/>
for any nodes that didn’t have a value assigned to them (actually in our case we wanted this where the value was 0). Apparently, in .NET if the node value expected back is of type Integer .NET won’t accept (or has trouble accepting)
<nodeName/>
I was a little unsure of how to generate an attribute with a namespace prefix using code. All credit goes to Graeme for coming up with the solution before me. Thought I’d post it here along with this great resource link.
Here’s how it was generated:
[as]
var n:Namespace = new Namespace(“xsi”, “http://www.w3.org/2001/XMLSchema-instance”);
xml.addNamespace(n); //where ‘xml’ is the variable your XML block is assigned to
if( initialMin == 0){
xml.initialMinimum.@n::nil = “true”;
}else{
xml.initialMinimum = initialMin;
}
[/as]
If initalMin is 0 it will generate this XML:
[as]
[/as]
I wanted to post my thoughts on this while I’m still fresh from a debate about XML structures.
Keep Your Application Dumb To Data Specifics (where possible!)
When structuring data for a view I am a keen believer that the view (in our case Flex) should know as little about data-specifics as possible. By this, I mean that we should strive to treat the incoming information in a generic way – as generic as we can, which of course varies from view to view but is worth taking a bit of time over. This may sound really obvious but in my experience I’ve occasionally had to put up a bit of a fight with those who provide an application’s data, to get the reasons across.
Ensure Correctly Placed Responsibility
By saying that I believe Flex should (as far as possible) “remain pretty dumb”, some take it as a work-avoidance tactic, or perhaps that one is not willing to harness the full power of Flex (“it can handle complex logic so why shouldn’t it?!”), but it’s really a case striving towards establishing correctly placed responsibility, ensuring that applications are receptive to change where most necessary. Sure, thanks to e4x (which is essentially XPath for ActionScript) we can do some powerful client-side parsing of data, but that doesn’t mean we should. If the front-end has too much knowledge about the data and that data changes it means changes will have to be made on the front-end AND on the back-end. Hopefully the example below demonstrates that this can often be avoided.
What I’m really trying to get across is that real power comes from being able to modify the data WITHOUT the need to modify the application, and seemingly small alterations to the format of your data can result in significant (easier-to-achieve) flexibility gains. After all, data is prone to change so we should code to accommodate changes to data. You may apply encapsulation techniques to your ActionScript code, but do you extend them to things like XML structures? If not, take a look at the example below in which I apply the rule “if it’s common to all nodes and likely to change, make it a ‘grouping’ node”:
The Example
In this example we wish to perform a very common task, displaying some grouped items in a DataGrid (as shown in this screen-shot):

This is going to be powered with XML, so here is a reasonable first-stab at the XML structure:
[as]
[/as]
Looks pretty standard. As you can see, each item has a “type” attribute and a “label” attribute. Even if the types were mixed up (XML makes no guarantee of order when dealing with nodes at the same level, and it shouldn’t either), we can easily retrieve types of the same value using e4x like this:
[as]
myXML.item.(@type==”single”);// returns an XMLList containing all “item” nodes with the matching type
[/as]
So what’s the main issue with doing that? It requires our application to have knowledge of the different types (note: See “Closing Point” near the bottom of this post for further discussion). Our code now has knowledge of values such as “single and “double”. What’s the problem with knowing these values? What happens when the data changes and we have a third or fourth type to deal with e.g. “triple” or “quadruple”?
[as]
[/as]
We may not have anticipated these additions when we started our project, but now we need to handle them, and if our code has knowledge of the types then it forces us to open up our project and make changes to the code.
Assuming our code has knowledge of the different types, it means our app will probably have some conditional logic to handle those types, and it is that conditional logic which will need to change in order to accommodate the new type(s). Conditional logic indicates a need to change something when that set of conditions alters, so while conditional statements are not bad in themselves, whenever you feel the need to insert conditional logic in your code, use it as a signal to take a moment out and re-examine how you’re approaching things (where and why you’re using a conditional, whether those decisions can be moved higher up the chain, and generally whether there may be a better way).
In this situation how can we avoid the need for our application to make decisions based upon the types here? One way would be to examine how we are dealing with our XML data. Is it absolutely necessary to know about the types in the first place? What advantages would be afforded by remaining dumb to what specific types we have, and how can we achieve that?
Find What Varies ….
Looking at the XML we can find what is most likely to vary, and change it from being an attribute, to being a parent node in its own right. By being a parent node it groups children with that specific value for us, and gives us a new node type which we can loop over. In our case the “type” is what is most likely to change. We may want to add more item types such as “quadruple”, or even take some types away (single-digit types are SO last-year), therefore we can extract the “type” attribute in to its own element/object – we’ll call it “itemType”. Here is the refactored XML:
[as]
[/as]
By doing this we have groupings by type, a small change but with a potentially big impact. Now our application can more easily be kept dumb to what types we have – all it needs to know is that there will be [n] itemType nodes each containing [n] items. We can easily code it so that, given a label to display and some data, it doesn’t care whether we have one itemtype or one hundred itemtypes, it simply displays the data that it’s given, and is capable of returning a list of selected ids or whatever the view is intended to do with these displayed items, while remaining “dumb” to what it is displaying. Now that is far more powerful.
We can handle the parsing of this kind of data in AS3 easily with a couple of nested loops and some e4x:
[as]
for each(var type:XML in xml..itemType)
{
createHeaderBar(type.@id.toString()); //Create your header bar for each type
for each(var item:XML in type..item)
{
//…loop through items within the parent itemType and do what you need to do
}
}
[/as]
Note that also, we now know the items will be grouped by type which means that all items of a particular type will be dealt with sequentially without us having to do any sorting, so if we want to add a header for each different type it’s really easy, we just do it the first time we deal with a new type, i.e. inside the outer loop as shown in the code above.
Closing Point
Without refactoring the XML we can still get around the need for knowledge about each type name by using a Dictionary object or HashArray to group our objects while still remaining ‘dumb’ to their types – we lose some of the power of e4x and add to the amount of work needed to group these types when the data arrives, but it can be done. However, there is the further argument that XML is intended to be data that is humanly readable. By creating the “itemType” node, a human consumer of that XML data can instantly see how items are intended to be grouped and more importantly get an instant indication of which values within that data structure are likely to vary at some point down the line.
So hopefully what we’ve ended up with is a piece of data which conveys more information and makes the task of keeping our application more flexible, easier. Of course this is not a post saying this is the only way to do things. Many times we don’t have finite control over the XML sent to us, but when we do we should advantage of that and take some time to consider the structure. It’s too easy to knock out some XML and think “yeah, that’ll do, XML is XML, it’s all the same”. I’ve seen many, many posts on when to use an attribute versus when to use an element in XML, but I’ve not really seen anything which puts forward key advantages of doing so from a programming perspective. I hope I’ve managed to do that a little in this post.
It’s such a huge topic and there are so many caveats but I’m always interested to get feedback and look at ways in which I can improve my consumption of data, so please feel free to chime in if you have any points to make.
Example & Screen Recording
This example of using Flex and the Presentation Model together is more complicated than the last two but similar enough that you can progress through from the previous example apps. Rather than expect you to just wade through the code, I’ve created an accompanying screen recording which highlights the main points. You can find it here – please excuse the initial mumbling; It gets clearer as it goes on, honest :)
[kml_flashembed fversion="10.0.0" movie="http://nwebb.co.uk/nwebb_example_code/presentationmodel3/View.swf" targetclass="flashmovie" publishmethod="static" width="330" height="320"]
[/kml_flashembed]
Views, Presentation Models & Data Models
In my client’s Flex project the view is bound directly to properties on a model. I’ve seen many projects work this way and guess it’s the de facto way of doing things:
1) The view fetches a particular model it needs from a “Model Locator” class.
2) The view components all bind to properties on that model.
3) The view calls model.getData(), and the model loads the data (via a delegate).
4) When the model properties update, the bindings are triggered on the view, and vice-versa.
In this particular project, data with identical rules (or indeed the same data) can be used in multiple screens, and because some underlying logic applies to that type of data in all circumstances, it was deemed that the DataModels should not only contain the data, but contain logic related to that data too. This means if some logic changes, there is only one class where changes need to be made.
Currently it looks a bit like this:
VIEW <-> DATAMODEL
However, if we now add in the Presentation Model, things look more like this:
VIEW <-> PRESENTATION MODEL <-> DATAMODEL
You end up with a class stuck in between the two. The main issue I’ve been having is how do I maintain a set up like we’ve got now (where changes to the view automatically update the data model and vice versa) while using this third class?
I’ve looked at some other Flex & PresentationModel examples, but while they load initial data via a delegate (read: DataModel), once the data is loaded, it seems to be the Presentation Model that holds this data and all changes to it. It also seems to be the Presentation Model which holds logic relating to the data. That measn repeating logic in multiple PMs … unless you extract the logic in to helper classes (remember, PMs are tightly coupled to particular views).
Example Code
In my above example, the data-logic remains in the DataModel class, presentation logic is in the PresentationModel class and View-only logic is in the View class (though there isn’t really any view-only logic in this example).
The example has a ModelData class, which gets updated in synch with the View, and a View which always reflects what is in the ModelData class. In Martin Fowler’s description he says the view should simply be a reflection of the PresentationModel, so perhaps I should regard this as something else entirely?!
In the example’s code comments, I’ve outlined some of the issues I encountered in more detail (see the DataModel class in particular), so remember to right-click and view the code!
Again, this is a learning exercise, certainly not a definitive “how to” and in all honesty by this point I am suffering from “code blindness” so I fear that I’m overlooking something hugely, glaringly obvious, so please feel free to laugh if I am … as long as you let me know and show me a better way to approach things :]
Also, if there is a framework out there which solves these particular issues let me know. The problem I find with these types of questions is that they are too long to post on a forum and many people won’t be interested or have a clue. If you’re still reading this I hope it’s a topic which does interest you, and hopefully you can impart your wisdom – essentially that is what I’m looking for from this post, a better way :]
Edit(s):
1) Yep, if you disable the top NumericStepper you can’t re-enable it. It’s thrown in to show how you can put PresentationModel logic in the PM – it has no real purpose so I didn’t bother implementing it fully (I just know someone will say “Hey, I can’t re-enable the input”) ;]
2) I forgot to mention: See how I put the logic which checks/sets the “inputStyle” in a getter (not a setter). That’s because, until the data has reached the model, it’s not been validated. In this example it was easy to stick it in the getter for ‘total’. I guess it would be better to have the DataModel dispatch an event which would be picked up by the PresentationModel … doing that in a getter is weird admittedly. There are several potential issues with this way of doing things. Feel free to highlight any you can think of.
3) As a potential way of handling validation errors in the DataModel, substitute the bbb setters for the ones below and then set the value of B to 81 or more (thanks Graeme):
[as]
// In DataModel:
public function set bbb(value:int):void
{
if (value > 80)
{
throw new Error(“bbb cannot be greater than 80″);
}
else
{
_bbb = value;
calculateTotal();
}
}
// In PresentationModel
try
{
_data.bbb = value; //SET ON MODEL
dispatchEvent(new Event(‘bbbChanged’));
dispatchEvent(new Event(‘totalValueChanged’));
}
catch (E:Error)
{
Alert.show(E.message);
}
[/as]
Here’s a quick tip I read about a long time ago, and it just came in handy (probably saved me some head-scratching) so I thought I’d pass it on.
Event.clone
If you’ve ever looked at the documentation for flash.events.Event you may have seen that the class has a method called clone which you can override.
The docs tell you that clone “duplicates an instance of an Event subclass”. You’ll rarely if ever want to call clone yourself. The EventDispatcher class calls it automatically for you when you redispatch an event. Let’s look at an example of where this info comes in handy:
Example – Refactoring a View
Today I was moving some code out of a view and in to a presentation model. However, everything is set up to listen for a particular event being dispatched from the views. I didn’t want to change the architecture and I didn’t want to make the existing Event-subclass bubble either, so instead, I decided to dispatch the event from the model, listen for it in the view, and then re-dispatch it from the view.
The code in my model looked something like this:
[as]
//IN PRESENTATION MODEL CLASS
var ev:StepEvent = new StepEvent(StepEvent.STEPNEXT); //create event
var stepCondition:StepCondition = new StepCondition(args); //this event passes along a condition object, so create…
ev.EventDataObject = stepCondition; //set the above object as a property on the custom event class
dispatchEvent(ev); //groovy chicken
[/as]
All the view needed to do was catch this event and pass it on up the chain (as if it has dispatched it itself):
[as]
//IN VIEW CLASS
_helper.addEventListener(StepEvent.STEPNEXT, onStepNext, false, 0, true);
…
…
private function onStepNext(event:StepEvent):void
{
dispatchEvent(event); //take event and pass it on
}
[/as]
However if I don’t override the clone method in StepEvent, I’ll receive a runtime error which will look something like this:
TypeError: Error #1034: Type Coercion failed: cannot convert flash.events::Event@abd16a1 to Tools.Process.StepEvent.
I get the error because EventDispatcher is automatcially calling clone, but as I have not overridden it in my subclass, the code in the Event superclass will execute; In the view the onStepNext method is expecting an event of type StepEvent but receives an event of type Event instead. This down-cast fails and an error is thrown.
The solution is to override the clone method, and ensure that the event being returned contains all the info which is specific to my custom event:
[as]
//IN STEPEVENT CLASS
override public function clone():Event
{
var ev:StepEvent = new StepEvent(STEPNEXT);
ev.EventDataObject = this.EventDataObject;//EventDataObject is the public property on my custom event which stores the condition object.
return ev;
}
[/as]
Now everything should work as expected.
Hope this helps.