February 04, 2007
Best Practice: Code Behind versus MXML Script Blocks versus View Helper
Often I see the question coming up on whether to use MXML Script blocks, Code Behind or Helper classes.
I think the question behind the question we're addressing here is not whether to use Code Behind, MXML Script blocks or View Helpers, but is actually where is it best to place functionality in a Flex application.
But first of all, let’s have a quick rundown on what Code Behind, View Helper and MXML Script blocks actually are:
If you come from the ASP world you might be familiar with Code Behind, if not more information about Code Behind can be found at the livedocs and at the devnet.
The Script block is the part of an MXML file dedicated to ActionScript methods and properties.
For example:
<mx:Script>
private function doSomething() : void
{
blah…
}
</mx:Script>
For a View Helper, people might have different definitions. Personally, I’d define a View Helper as an ActionScript class that a MXML can call and listen to directly. Typically it contains the code related to the accompanying MXML and might even simply contain the functionality that developers otherwise would put in MXML Script blocks. It can have references to the view, such as the Cairngorm View Helper (which are not a recommended approach). I’d call ActionScript classes that help the view with more defined and fewer responsibilities utility classes. Utility classes usually don’t have references to views. They could i.e. be formatters.
Personally I find Code Behind is indeed a nice and quick way to organize functionality. However, I’d argue that if you would move code from a MXML Script block into a Code Behind class, you’re just changing the location and potentially rough syntax of that code. After this “refactoring”, your code still lives in the same context.
In a middle and larger scale application i.e. using Cairngorm, I’d advocate extracting functionality from views into unit-testable model and utility classes. IMHO such classes are best created when they don’t contain any references to views and few responsibilities. They should be simple ActionScript classes without dependencies on other contexts such as view components.
I’m not saying you couldn’t unit-test Code Behind classes. It is like unit-testing Cairngorm View Helpers, people have done this and are doing it successfully. However, I just find it easier to unit-test and also reuse classes that contain defined functionality adhering to OOP rules. If you keep your functionality in view code, this functionality often has much more responsibilities to keep track of.
Once you're focusing on extracting the right functionality from your views and commands into unit testable utility classes and an application model deciding if you'd rather have Code Behind, View Helper or an MXML script block becomes less of an important decision to take. Once you've extracted functionality out of views, the view code left, isn't usually that big. Just converting this view code into a different syntax (Code Behind, View Helper or MXML script block) doesn't necessarily make it much better.
Posted by auhlmann at 10:56 AM | Comments (10)
September 28, 2006
Using Binding Securely
In my last post about the Cairngorm Dashboard example I’ve added a little functionality that allowed a view to react to a state change in the model in order to do something view related like invoking a popup or an effect.
I’ve been using the binding approach and that made it very easy and flexible to do. But as I said in my last post, this can have one slight drawback you have to consider. In this post I’ll showcase the drawback and provide a solution to it via an extended version of Paul’s Observe tag. Furthermore, this extended version of Observe makes it even easier to perform this kind of listening to the model.
Behind the scenes, binding uses flash.events.EventDispatcher. But binding gives you much more than just that. You can bind to properties even when their objects aren't initialized yet. You can also bind to XML data using E4X to specifically listen to certain areas of the XML. For more information on the features of binding make sure you read the Chapter 37 in Part 5 of the Flex 2 Developer’s Guide.
There are multiple ways to use binding. All of them require a source that defines what property you want to listen to and a destination that defines what shall be triggered once your source changes.
You can either use the MXML curly braces syntax, function binding, the mx:Binding tag or define bindings in ActionScript.
Either way, you have to be careful in triggering application code in a destination of a binding, which we've done in previous examples of my Dashboard sample using either function binding to trigger a formatter or mx:Binding to invoke a view related method.
Since the Binding feature hides us from many exceptions that would have been thrown using EventDispatcher, it cannot differentiate in certain situations when certain; to us important, exceptions are thrown in our application code that is triggered in the destination. Let's showcase this and throw one of these "certain" exceptions on purpose in our latest Cairngorm Dashboard sample. Let's throw one that I'm sure you've have seen before: The one and famous:
ErrorID 1009: "Cannot access a property or method of a null object reference."
Change the createChargedMessage setter to:
private function set createChargedMessage( value : Boolean ) : void
{
var o1 : Object = {};
o1.o2 = {};
trace( "show o2 " + o1.o2 );
o1.o2 = null;
o1.o2.foo = "bar"; //oops, o2 doesn't exist, an exception should be thrown.
trace( "show foo " + o1.o2.foo );
if( value ) Alert.show( "Thanks! Your bank account has been charged" );
}
Since this code is triggered as a destination of the mx:Binding tag, the runtime exception that should appear on
o1.o2 = null; o1.o2.foo = "bar";
is swallowed by the binding code in the Flex framework. You will not see the usually expected runtime exception displayed via the Flash Debug Player. Instead, the following code like the trace( "show foo " + o1.o2.foo ); will just not execute.
Note, that this will only happen with certain exceptions. It will only happen on the following exceptions:
- Error #1006: Call attempted on an object that is not a function.
- Error #1009: null has no properties.
- Error #1010: undefined has no properties.
- Error #1055: - has no properties.
- Error #1069: Property - not found on - and there is no default value
For more information on these errors take a look at Run-Time Errors of Adobe Livedocs.
Note also, that this will only happen when your application code in the destination executes in the same ActionScript block. This means if your exception throwing code executes in a different frame (as would usually be the case on a custom effect) you will see a runtime exception as expected.
However, if your application code does throw one of the swallowed exceptions in the same ActionScript block, there are ways so you can see this immediately.
You could use a special debug class in the Flex framework to help you see the error message. Use
mx.binding.BindingManager.debugBinding( <your destination as a String> );
But in a production environment I'd recommend you use another more bullet-prove way of solving this problem. You can use the extended version of Paul’s Observe tag.
<ac:Observe
source="{ stockTransaction.stockPurchase.isComplete }"
handler="{ createChargedMessage }"/>
Using Observe you can invoke a method instead of the setter.
private function createChargedMessage( value : Boolean ) : void
{
if( value ) Alert.show( "Thanks! Your bank account has been charged" );
}
Try putting the code from above that will throw a runtime exception into the createChargedMessage method. Observe will make sure that a runtime exception is being thrown as expected.
Internally, Observe wraps another global Error handler around your destination, saves the exception thrown and throws it in the next frame.
Furthermore this approach offers an additional feature for your convenience. If you want to listen to only a specific value of the source, you can use ObserveValue for this. In our use case of the iteration 6 Cairngorm Dashboard we just want to know when the isComplete property of our StockPurchase object is true.
<ac:ObserveValue
id="observe"
source="{ stockTransaction.stockPurchase.isComplete }"
handler="{ createChargedMessage }"
value="{ true }"/>
private function createChargedMessage() : void
{
Alert.show( "Thanks! Your bank account has been charged" );
}
I've mentioned above that this also applies to function bindings. In StockMarketPod.mxml of the iteration 6 Cairngorm Dashboard sample application, I've used the provided Observe.execute method in order to ensure a secure use of our function binding, which tirggers a custom formatter.
<mx:Label text="{ observe.execute( statusFormatter.format, stockTransaction.status ) }"/>
You can download the Observe and ObserveValue utility classes from here.
Hope this helps!
Posted by auhlmann at 11:58 AM | Comments (16)
