Cairngorm Sample – How Business Logic Can Manage Views Part IV

In the last post about this topic, I’ve added some functionality and another use case to our stock quote application and showcased how a Cairngorm application could scale with focusing on creating model objects. In this post I’ll perform some further refactorings to our model objects.
Furthermore, our customer wants us to show a pop-up as soon as a successful stock purchase is being made. I’ll cover how a state change in your model can transparently cause view related code (like creating a pop-up) to be executed.


Iteration 6 – Refactorings
In iteration 5 we had a statusMessage field in both model objects, StockQuote and StockPurchase. Two separate text input fields have listened to these properties. Now, let’s only have one status message for both model objects. This i.e. could bring us to the idea of creating another object that encapsulates StockQuote and StockPurchase. Let’s call it StockTransaction:

package com.adobe.cairngorm.samples.dashboard.model
{
public class StockTransaction
{
[Bindable]
public var stockQuote : StockQuote;
[Bindable]
public var stockPurchase : StockPurchase;
[Bindable]
public var status : int;
public function StockTransaction()
{
stockQuote = new StockQuote();
stockPurchase = new StockPurchase( stockQuote );
}
}
}

Note that in the above, I’ve changed the previous statusMessage property; which was typed as a String, to a status property; which is typed as an int. I’ve done that so our model objects can only be concerned with a status of our use case and not with the presentation of this status. I think the presentation of the status is something that our views should be responsible for.
Our model objects can contain the different status options they can be in. i.e. our new StockQuote object can have an ERROR and a SUCCESS status, showing that the StockQuote retrieval was either successful or not. So, we can define the states with two values.

public static const ERROR : int = 1;
public static const SUCCESS : int = 0;

In our new GetStockQuoteCommand we can decide in what state the StockQuote object is currently in. Here’s a snippet of it:

public function onResult( event : * = null ) : void
{
//for demo purpose: event would normally be an event object of remote service result.
stockTransaction.stockQuote.lastStockQuote = event as Number;
stockTransaction.stockQuote.isPending = false;
stockTransaction.status = StockQuote.SUCCESS;
}
public function onFault( event : * = null ) : void
{
stockTransaction.stockQuote.lastStockQuote = NaN;
stockTransaction.status = StockQuote.ERROR;
stockTransaction.stockQuote.isPending = false;
}

There are various ways to achieve a similar separation of concerns. In this application however, I’ll let the StockMarketPod.mxml view listen to the value of status and depending on the status value, StockMarketPod will display a String, which is defined on this view. StockMarketPod uses a helper class called StatusMessageFormatter that translates the status property into the correct String needed by the view. This helper class is initialized like this:

<view:StatusMessageFormatter
id="statusFormatter"
statusMessages="{ [ '', 'Quote retrieval error.', 'Purchase error.', 'Purchase succesfull.' ] }"/>

I listen to the status via a function binding on the status property. For more information on this you can take a look into the full source code for this application at the end of this post.
Flex 2 make it easy to perform refactorings like this. This is very important since refactorings of your model objects can occur all the time and most often you rarely get it right the first time around. ;)
Iteration 6 – Invoking View Related Methods
As said in the beginning of this post, our customer wants us to alert the user via popup when a stock purchase request has successfully been made. Let’s think of creating a simple Alert window (mx.controls.Alert). When the onResult handler of the PurchaseStockCommand is invoked we know a stock purchase has been completed.
I think of creating an Alert control as something only views should be concerned about. It’s like creating other view components, managing states of view components or effects. I think only views should be responsible. Usually we bind UI controls to properties of our model objects. But for creating an Alert 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?
I basically see two ways to do this from a model. We could manipulate a model object so it dispatches an EventDispatcher event that the view can listen to. We could also just mark properties of our model as Bindable and let our views subscribe to either a custom event name that you have defined in our Bindable metadata (with [Bindable(event="customEventName")]), or to the default “propertyChange” event. Using the propertyChange event your view’s event handler method would have to differentiate what property changed. You can do that with the passed in mx.events.PropertyChangeEvent object. Listening to a custom event name defined in the Bindable metadata, we have to dispatch the event manually using dispatchEvent.
I see two drawbacks using this approach. First of all, it’s more code to write and most importantly, to read. This is because your views need to subscribe manually to events and define event handlers. It might also affect your models since some of them might need extra EventDispatcher functionality. Secondly, we need to make sure that our model objects exist when the view is subscribing to the event.
The latter is something binding gives us for free. We can bind to a state change of a model even if the model isn’t initialized at the time of subscription. Furthermore, using the binding approach is just quicker to do in many cases.
Following the binding approach, we can bind to a property of our model objects and invoke a method in our views with using the mx:Binding tag.
This could look like the below:

private function set createChargedMessage( value : Boolean ) : void
{
if( value ) Alert.show( "Thanks! Your bank account has been charged" );
}
<mx:Binding
source="stockTransaction.stockPurchase.isComplete"
destination="createChargedMessage"/&gt

Now, instead of using mx:Binding I recommend that you use an extended version of the Observe tag that Paul Williams originally blogged.
This can make your code more secure, and furthermore it does also give you a slightly easier API to solve the problem here. We only need to listen to the “true” value of the isComplete Boolean. Therefore, we tell our new utility exactly this.

<ac:ObserveValue
id="observe"
source="{ stockTransaction.stockPurchase.isComplete }"
handler="{ createChargedMessage }"
value="{ true }"/&gt

The createChargedMessage will be reduced to this:

private function createChargedMessage() : void
{
Alert.show( "Thanks! Your bank account has been charged" );
}

For more information on why I’ve introduced this utility class, please read the follow up post on this topic, “Using Binding Securely”.
Summary
What have we done here?
We needed our view to invoke a view related method based on some state change in our model. First, we’ve changed the state in the model with invoking a Command. The Command located our model object via ModelLocator and changed the state of this model object via its API. Because our view subscribed to this specific state change (via binding in our case), it could react to it with invoking a method. Since it was using mx:Binding, we didn’t need any extra ActionScript to setup the binding, neither did we need a MXML component that would accept a return value (which we didn’t have since we were calling a popup).
As I’ve noted above, there’s also one little drawback on how we use the binding approach here.
For more information on the drawback and a solution to it with an additional feature please read the next post, “Using Binding Securely”
As after every post in this series, you can view the application and download the source code of it via right mouse click > View Source.