« November 2006 | Main | March 2007 »
February 12, 2007
Creating a Popup in a Cairngorm Architecture
Often I see this question coming up:
"How should I best create a popup in my Cairngorm application?"
I think of creating a popup control as something only views should be concerned about. It’s like creating other view components, managing states of view components or effects. Therefore this question isn't just about creating popups, instead it's about how to invoke methods that belong to the view. (BTW: I often hear from our User Experience team that they try to prevent popups where possible)
First and foremost, I’d try to let your views directly call a method that dispatches a popup.
However, most of the use cases that came up when questions like the above have been asked are slightly more complex. Users for example want to create a popup in a completely different part of the application. Or they receive an asynchronous remote service response, only want to react to the user with a popup at that point in time and work in a context where no view references are available, such as a typical Cairngorm Command.
Some users have created the code needed for creating a popup directly in the result method of a command or in a model object that’s been called by the result method of a command.
Sticking to my above rule; that only views should be concerned about popups, I think both solutions are not the optimal approach. A command should just be an encapsulated request to your model and a model itself should also not be concerned about any UI behavior. I’d argue we need to tell the model about the state change that has just occurred in your application, but in a model context. For example you might set a loggedIn property on a Login model object to true in order to signal that the login process was successful after a positive remote service response.
So, how can our views react to that state change in our model?
In a typical Cairngorm application, we may bind UI controls to properties of our model objects. But for creating a Popup control, there’s no UI component where we can bind to. How do we best let a view invoke a view related method after a certain state change in our model occurred?
Let’s take a step by step approach using a very simple Cairngorm application, that retrieves a list of employees and displays them in a popup after successful retrieval.
Please do ask your UX team if something like this is really a good idea in a real world application. For the sake of this example we don’t care about the user experience here!
This step by step tutorial will walk you through the relevant parts. You can see and download (via right click > View Source) the complete application here. Let's go:
Dispatch a Cairngorm Event.
Let the view dispatch a user gesture via a Cairngorm Event in order to trigger the remote service:
On a mx:Button’s click event, the getEmployees method dispatches the Cairngorm event:
Excerpt from GetEmployeesCommand.as:
private function getEmployees() : void
{
var event : GetEmployeesEvent = new GetEmployeesEvent();
event.employees = model.employees;
CairngormEventDispatcher.getInstance().dispatchEvent( event );
}
Call and handle the remote service and modify your model.
The handling GetEmployeesCommand’s execute method calls a server side method. Its result method modifies the model. Here, it retrieves the model object Employeee via the ModelLocator.
Excerpt from GetEmployeesCommand.as:
private var employees : Employees;
public function execute( event : CairngormEvent ) : void
{
employees = GetEmployeesEvent( event ).employees;
employees.hasEmployees = false;
var delegate : EmployeeDelegate = new EmployeeDelegate( this );
delegate.getEmployees();
}
public function result( event : Object ) : void
{
employees.employees = IList( event.result );
employees.hasEmployees = true;
}
Employees.as:
package com.adobe.cairngorm.samples.popup.model
{
import mx.collections.IList;
public class Employees
{
[Bindable]
public var employees : IList;
[Bindable]
public var hasEmployees : Boolean;
}
}
Note that we modify the hasEmployees property of the Empoyees model object.
Let the view react react for you!
And here comes the crux: You can use the mx:Binding tag or the Observe/ObserveValue tag to invoke a view method, once the hasEmployees Boolean value changes.
I’d recommend using the ObserveValue tag to listen to a specific value of a state change of your model. More precisely, you can bind the source property of it to the bindable hasEmployees property of your model object Employees.
<ac:ObserveValue
source="{ model.employees.hasEmployees }"
handler="{ createEmployeeList }"
value="{ true }"/>
You could have also used the Observe tag to listen all updates of the hasEmployees property. For more information, check this out.
The ObserveValue tag above will invoke the createEmployeeList method defined in a Script block of the same MXML file. This method will invoke the popup.
private function createEmployeeList() : void
{
var application : DisplayObject = DisplayObject( Application.application );
var popup : IFlexDisplayObject = PopUpManager.createPopUp( application, EmployeeList, true );
PopUpManager.centerPopUp( popup );
var concretePopup : EmployeeList = EmployeeList( popup );
concretePopup.employees = model.employees;
}
That's it! Through a state change in your model, you view has reacted. Furthermore, you can now let many other objects observing this particular state of your model and they can all act independently.
Cheers!
Posted by auhlmann at 02:02 PM | Comments (19)
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)
