Manipulating Views under Cairngorm using Facades

We recently re-wrote a fairly major Flex application, having an opportunity to start from scratch with lessons learned. The previous implementation used the Cairngorm framework, but not quite in line with its intent — it was purely used to in “data commands” to retrieve data from the server.

After reviewing some of the other MVC frameworks out there (PureMVC mostly), we decided to stick with Cairngorm but to expand its use to the full extent it was intended. In our mind, that meant to have Cairngorm events/commands handle all user gestures (unless they were trivial, component-local gestures like re-sorting a datagrid, etc.). This worked great, and we were pretty happy with the emerging structure of the application except for one thing: we didn’t like the ViewHelper/ViewLocator pieces. It seemed too cumbersome and overly structured for the simple task of having a command communicate with a view. We also didn’t like to control views from commands via the ModelLocator (as also suggested by the Cairngorm docs), as we had tried that in the earlier version and it quickly became a disastrous mess of spaghetti bindings that were incredibly hard to unravel, much less maintain.

To make this process simpler, we came up with a different approach: view facades. These facades are essentially ViewHelpers, but we eliminated the ViewLocator by making the facades singletons attached directly to the view. A sample makes this clearer. Consider a command GetBookDetails. We want this command to contact the server and get the details for a particular book. During this process, the view should show a progress or “loading…” view, to be switched out for the actual book details view when the loading is complete. That command could look like this:

    public class GetBookDetailsCommand implements ICommand {
 
        public function execute(event:CairngormEvent):void {
            var bookEvent:GetBookDetailsEvent = event as GetBookDetailsEvent;
 
            BookViewFacade.getInstance().showLoadingView();
 
            // Load the book details from the server here, using a custom HTTPServiceEvent of ours...
            var bookServiceEvent:HTTPServiceEvent = new HTTPServiceEvent('book_get', BookModelLocator.getInstance(), 'book', 'book');
            bookServiceEvent.params = {id: bookEvent.bookId}
            bookServiceEvent.addCommandStatusListener(onBookResult);
            bookServiceEvent.dispatch();
        }
 
        private function onBookResult(statusEvent:CommandStatusEvent):void {
            BookViewFacade.getInstance().showBookDetailsView();
        }    
    }

Notice the two calls to BookViewFacade, controlling the visible view in our BookView component. That component (BookView) would look something like this:

. . .    
    <view:BookViewFacade id="facade" bookView="{this}"/>
 
    <container:ViewStack id="viewStack" width="100%" resizeToContent="true">
 
        <container:HBox id="VIEW_LOADING" width="100%">
            <control:Label htmlText="Loading..."/>
        </container:HBox>
 
    	<container:VBox id="VIEW_BOOK_DETAILS" width="100%">
            <control:Label htmlText="{MainModelLocator.getInstance().book.title}"/>
            <control:Label htmlText="{MainModelLocator.getInstance().book.title}"/>
            . . .
    </container:ViewStack>
. . .

Notice the declaration of the view facade BookViewFacade, where the view references itself. The facade is a singleton, implemented along these lines:

    public class BookViewFacade {
 
        public var bookView:BookView;
        private static var instance:BookViewFacade;
 
        public function BookViewFacade():void {
            instance = this;    
        }
 
        public static function getInstance():BookViewFacade {
            return instance;
        }
 
        public function showLoadingView():void {
            bookView.viewStack.selectedChild = bookView.VIEW_LOADING;
        }
 
        public function showBookDetailsView():void {
            bookView.viewStack.selectedChild = bookView.VIEW_BOOK_DETAILS;
        }
 
    }

The view is thus abstracted from the commands, but can be readily manipulated by singleton-access to the view facade and its methods. You can then easily work with views from within the Cairngorm framework, without the need for maintaining ViewLocators, etc.