Cairngorm Secret Tip #3: Responders for View Notifications

As Flex continues to grow as the choice RIA technology, many developers start investigating and apply Java design patterns to Flex implementations. Similar to Java Struts, Cairngorm is a MVC (Model-View-Controller) framework – endorsed by Adobe – that is widely popular within the community.

One of issues that frequently confuses, blocks, or complicates Cairngorm implementations is the issue of “view notifications”. Every Cairngorm developer ask the following question at some point: How should the Cairngorm business layer best communicate with the Presentation layer?


Review Figure 1 which illustrates the recommended relationships between the view and control layers[1]:

Flex Cairngorm MVC

  • Model is a repository for data
  • Views support user interactions and announce user gestures as dispatched events to Controllers
  • Controllers employ commands to process dispatched events
  • Commands are responsible for using Delegates to talk to the server, and updating model data (client side)
  • Views use data binding to “watch” Model data changes and self-update to dipslay formatted data.
This sounds great. So what is the problem?

Commands are supposed to use the Model layer as the mediator between business and presentation layers. In Flex, data bindings and change watchers allow views to watch data models. When data is modified, hidden propertyChange events are dispatched from Models to Views and the View uses event listeners to self-update. This is the basic premise of the Flex Cairngorm MVC framework.

In real-world applications, data binding solutions espoused by Cairngorm MVC are not always sufficient; since such solutions require views to watch for data changes. Some times, however, watching for data changes is complicated… perhaps the data is not globally available. Sometimes the view [that dispatches the business event] is requesting data that should be available only to that specific instance of the calling view; and should not be stored in global repositories. Sometimes, we do not care so much about the data… but instead we want to execute custom code when the event succeeds and finishes.

PureMVC and traditional Cairngorm solutions require complex code and design patterns to solve these situations.

  • ViewLocators could be used to try to lookup an instance of a view. (Requires coupling to specific view classes from the business layer…. BAD!)
  • Views could employ/register private view controllers with the front controller,
  • Command could dispatch new “notify view” events thru the front Controller to the view controller

The above approaches create a proliferation of view controllers and is useful ONLY when multiple views want to listen to the same event and be notified simultaneously. Please note that in such rare situtations, a custom event-bridge or a traditional data binding approach would be more practical [but that is another article]. In fact, in most scenarios, the view simply wants the notification directly and exclusively (only to its instance). For these situations, there is a simpler – and better – solution to the ViewController complexity.

  • What if we cannot easily use global data models and view data binding?
  • What if we do not want to create an hierarchy of bindable properties simply to deliver data to deeply nested child views?
  • How can we deliver data directly to a view instance without storing that data in the ModelLocator hierarchy?
  • And… if the View invokes a Command [via event dispatching] and Command/Delegate pairs process server data, how can the Command deliver the processed results back to the View only?

The remarkably easy solution is to use Responders as notification mechanisms to allow Commands to notify Views… just like the way Delegates notify Commands [See Cairngorm Secret Tip #2: Data Transformations within Delegates]. To understand the proposed solution, consider the Cairngorm approach where a Command instance employs a Delegate class as “API facade” to remote data services.

The command instance submits itself [or a proxy to itself] as a responder to the delegate. The delegate, in turn, simply uses the specified responder to direct dataservice responses directly to back to the command. This allows the Command to receive callbacks from the Delegate or the Server. In fact, advance this same approach is used at the Delegate level to allow Delegate instances to intercept server responses, transform data, and then notify/pass the modified data back to the Command.

With Responders, post-command notifications can be delivered agnostically to any caller [View] that has provided a ResultEvent handler and a FaultEvent handler.

Using Responders, a Command can submit itself as a callback to specific Delegate calls. Views can effectively do the same with Commands. Figure 2 below illustrates this process or queueing of responses. The queue builds with (1) View waits for a response from the Command, (2) the Commands waits on the Delegate, and (3) the Delegate waits on the Server. The queue unwinds after (4) the server responds, (5) Delegate notifies the command, (6) the Command notifies the view[2]. Using a Responder proxy, the view is notified after the actual dataservice request/response and after Command processing; see Step (6) in Figure 2.

View Notifications using Responders


 

How exactly does the view provide a Responder to the Command ?

Well, when we dispatch business events to commands, we create custom events. So we simply need to transport a Responder instance along with the normal event information that is dispatched to the Cairngorm control layer. If all custom events have constructors with optional responder arguments then we have a built-in mechanism to support callbacks from Commands.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package mx.rpc
{
/**
 *  This interface provides the contract for any service
 *  that needs to respond to remote or asynchronous calls.
 */
public interface IResponder
{
	/**
	 *  This method is called by a service when the return value
	 *  has been received. 
	 *  While <code>data</code> is typed as Object, it is often
	 *  (but not always) an mx.rpc.events.ResultEvent.
	 */
	function result(data:Object):void; 
	/**
	 *  This method is called by a service when an error has been received.
	 *  While <code>info</code> is typed as Object it is often
	 *  (but not always) an mx.rpc.events.FaultEvent.
	 */
	function fault(info:Object):void;
}
 
}
package com.grdlinked.samples.control.events {
 
    import flash.events.Event;
    import mx.rpc.IResponder;
    import com.universalmind.cairngorm.events.UMEvent;
 
    public class LoadUserDetails extends UMEvent {
 
        public static var EVENT_ID : String = "loadUserDetailsBySSN";
 
        public var ssn      : String = "";
        public var password : String = ""; 
 
        public function ApplicationInitializeEvent( ssn:String,
                                    password:String,
                                    callbacks:IResponder=null) {            super(EVENT_ID, callbacks, true, false);
 
            this.ssn      = ssn;
            this.password = password;
 
        }
    }
}

For the examples in the post, we use a custom event LoadUserDetailsEvent class that accepts an optional “responder” parameter. Using a SSN and password the view requests user details from the control layer. The control/business layer may elect to lookup the user in a client-side cache or call the server. The business layer is responsible for all these details and the presentation/UI layer need not worry. And the view does NOT know anything about who handles the event or how the event is processed. This “separation of concerns” is what MVC is all about.

When the event is processed by the command, the command can temporarily cache the view responder or callback. Sometime later – after the asynchronous data service call responds – the following can happen:

  1. the command can use the dataservice response to update the ModelLocator directly, trigger databindings to update, and
  2. the command can forward the dataservice response or any OTHER data to the cached responder. Command::notifyCaller() is the method used to provide view notifications.
Do all my views have to implement the IResponder interface or – even worse – extend a custom parent class?”

If your views want to implement the IResponder interface, each one of your views would need to include public result() and fault() functions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas 	width="400" height="300" 
		implements="mx.rpc.IResponder"
		xmlns:mx="http://www.adobe.com/2006/mxml" >
 
	<mx:Script>
	<![CDATA[
		import mx.rpc.IResponder;
		import mx.controls.Alert;
 
     // Methods required by the IResponder interface		
      public function result(data:Object):void {
            btnLoad.enabled = true;
      }
      public function fault(data:Object):void {
            Alert.show(event.message);
            btnLoad.enabled = true;
      }
 
 
	// Private methods invoked by child controls		
      private function loadUserDetails(ssn:String):void {
            btnLoad.enabled = false;	// disable button while waiting for a response.
 
            // Load the user details for the specified user
            //NOTE how this view will be the responder...
            var callbacks : IResponder = this as IResponder;            var event : LoadUserDetailsEvent = new LoadUserDetailsEvent("435-34-1283", "anonymous", callbacks);
		  event.dispatchEvent(  );   // Cairngorm events self-dispatch directly to the business layer; 
      }
 
	]]>
	</mx:Script>
 
	<mx:Button id="btnLoad" label="Load User" textAlign="center" x="145" y="149" click="loadUserDetails()" width="147"/>
 
</mx:Canvas>

In our example the view needs to know when the results are available; since the load button is disabled until the business layer [and server] responds [3].

The above code sample shows, unfortunately, how each of your custom views would need to be modified to work as view responders. When the command finishes, it would call something like caller.result(). Unfortunately, the signature of the result() function is ambiguous and does not document which event triggered the result. In contrast a method like onResult_loadUserDetails() explicit documents its purpose.

If you see any code like that show above, then please slap the developer immediately!

After a thorough slapping, ask that developer this question:
“What if I had two buttons that each dispatched separate business events and each needed separate response handlers? How could the result() function be the common event handler for both events? Would I have to somehow detect – inside the result() method – which event is responding?”

As you can see, this solution rapidly creates nasty code and rapidly produces nasty, convoluted, unmanageable code.

So instead of requiring an abomination of view interfaces or view inheritance (which is even worse), the view will instead create an instance of a Responder class that will serve as a proxy responder class. The Responder class will redirect result() and fault() calls to methods that we specify. [4] Let us look at some code for clarity. Below we see an example of custom view component created to load and display user details.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas 	width="400" height="300" 
		xmlns:mx="http://www.adobe.com/2006/mxml" >
 
    <mx:Script>
    <![CDATA[
		import mx.rpc.IResponder;
		import mx.controls.Alert;
		import mx.rpc.events.FaultEvent;
		import mx.rpc.Responder;
 
 
     private function loadUserDetails(ssn:String):void {
          // Load the user details for the specified user
          var callbacks : IResponder           = new Responder(onResults_loadUserDetails, onFault_loadUserDetails);          var event     : LoadUserDetailsEvent = new LoadUserDetailsEvent(user.ssn, user.password, callbacks);
 
          btnLoad.enabled = false;	// disable button while waiting for a response.
          event.dispatchEvent(  );	// Cairngorm events can self-dispatch to the business layer.	  
     }
 
     // ***************************************************
     // Private EventHandlers for the loadUserDetails()
     // ***************************************************
 
     private function onResuls_loadUserDetails(user:UserVO):void {
          // The SSN and password were authenticated; here are user details
          _user = !user ? new UserVO() : user;
          btnLoad.enabled = true;
     }	
 
     private function onFault_loadUserDetails(event:FaultEvent):void {
          Alert.show(event.message);
          btnLoad.enabled = true;
     }
 
     private var _user : UserVO = new UserVO();			
 
    ]]>
    </mx:Script>
 
	<mx:Text text="Please enter your credentials to edit your account:" x="16" y="30"/>
 
	<mx:FormItem label="FullName:" x="23" y="208">  
		<mx:Label text="{_user.fullName}"/> 
	</mx:FormItem>
	<mx:FormItem label="SSN:" x="54" y="104">  
		<mx:TextInput id="txtSSN"  text="{_user.ssn}" width="129"/> 
	</mx:FormItem>
 
	<mx:FormItem label="Password:" x="23" y="70"> 
		<mx:TextInput id="txtPassword" /> 
	</mx:FormItem>
 
	<mx:Button id="btnLoad" label="Load User" textAlign="center" x="145" y="149" click="loadUserDetails()" width="147"/>
 
</mx:Canvas>
Question: And how is the view notified of the response from the server (after the command finishes)?

Commands – after the processing is finished – will trigger any cached IResponders::result() methods to notify the sender [View] of the event.

In the code example above, look at line 15 where we use a Responder instance to act as a proxy for the functions that we want to call. The Responder class does NOT know any details about the view itself. It simply has functions that may be called upon an event response. For a successful response, the view wants onResults_loadUserDetails() to be called. Likewise for failures, the view wants onFault_loadUserDetails() to be called. With responders, the view essentially provides event handlers for Command post-processing.

To summarize the above code snippet as psuedo-code, you (a) use a responder proxy wrapper class, (b) package the responder instance inside the event, and (c) extend the command base class to support the caching of the callback/view responder. And voila’ you are done!

This is VERY clean:

  1. your views are still standard mxml or as classes with aggregates and composite controls,
  2. event dispatching is the same [with an optional callback] and
  3. you do NOT have to create crude view controllers to handle view notifications
  4. each view instance can have its OWN, separate callback
  5. views can then support modal operations, undos, reverts, commits, etc… completely independent of each other.
  6. you can dispatch as many business events as desired; each with their own event/response handler methods.

Better yet, this functionality is built into the Cairngorm Extensions (CGX) using both the Callback and the mx.rpc.Responder classes. CGX is an open-source extension to cairngorm. If you are interested in this and other Cairngorm extensions provide comments to this blog or or visit Google Cairngorm Extensions.

GD Star Rating
loading...

  1. Note that the Control layer is also known as the business layer. []
  2. Steps 5, 6 are optional. Responders can be configured so server responses go directly to Commands. And 6 will be ignored if the caller has not submitted the optional responder []
  3. Please note this is a trivial example using a disabled button requirement… I leave it to your imagination for more sophisticated usability and response needs []
  4. Much like addEventListener(eventType, eventFandler,…) syntax, the Responder uses syntax like new Responder(resultHandler,faultHandler=null). []

30 Responses to “Cairngorm Secret Tip #3: Responders for View Notifications”

  1. Victor Moya says:

    Hi Thomas,

    Thanks for your post! that’s exactly what I was looking for.

    Anyway, I missed somewhere to download the swc file with the cairngorm extensions you’re using there and, as many other guys commented, a code sample of the command class.

    I’ve been digging a little more on that questions and I found a very simple example of an application using (I think) exactly all that theory you explain so well. Here’s the link:
    http://nagpals.com/blog/post.cfm/hello-world-using-cairngorm-extensions

    You can click in “View application” to see it working and then click “View source”. Also, if you download the example, you get the SWC file with the Cairngorm Extensions.

    I’m going to try to use in a little example and see if it works.

    Thanks once again and be sure I’ll stay tuned for upcoming posts

    • thomasb says:

      Victor,
      While the View source tree does NOT show the libs, the downloadable zip archive has the LocalizationMap source and the .SWCs in the /libs/*.swc.
      The Cairngorm swc is included in the archive. Click this link: The Cairngorm Extensions to browse the online source code for the CGX swc.

  2. Nancy says:

    Thanks for the posting. The title of the article is Cairngorm Secret Tip #3 …
    Do you have other postings, such as #1, #2?

  3. Mahesh says:

    The best tutorial i have read so far.

  4. Social comments and analytics for this post…

    This post was mentioned on Twitter by ryancanulla: #cairngorm, heres a great article on using IResponder to pass view notifications http://bit.ly/2Keuls...

  5. eyu says:

    Great article.
    This saves the new “notify-view” event generation/dispatch from the command to the view.
    Now we have a use case that several views are listening to the “notify-view” event generated by the command. But in your example, it seems only one callback responder can be specified in the original event. Can you suggest a way to resolve our issue?
    Thanks!

    • thomasb says:

      Eyu, you have made an excellent an astute point. True, callbacks is a 1-to-1 connection between the request originator and the response. If you have a need for multiple callbacks (e.g. more than 1 listener wants to be notified), then use either the ModelLocator databinding approach OR you can create a special dispatcher that supports originators to register for events… think of this an “event bus/pipeline” for your special needs. Unfortunately event bus architectures are ALWAYS specialized and are beyond the scope of a blog. :-)

  6. diamondTearz says:

    Thanks for writing up this clear explanation. And of course, thanks for clearing up some of the details of using the UM extensions a while back at the Online Flex meetup. It has helped my Flex workflow greatly.

    Mani

  7. Love the article, but it would be nice to see what the command class looks like, or at least a link to an example.

    If I am understanding this correctly, since the responder is optional on the event, the command class would have to check to see if it is defined before calling it to avoid a null pointer exception correct?

    So would a command result function notifying a view responder look something like this?

    public function result(data:Object):void{
    //update model
    /*I assume responder is stored a property on the command? */
    if (this.responder){
    this.responder.result(data);
    }
    }

    Jeremy Wischusen’s last blog post..Adobe Flex Frameworks Article

    • thomasb says:

      This is why the CGX (Cairngorm extensions) Command class has a notifyCaller() method. That method checks for a valid callback closure. Don’t forget to call super.execute() in the Command::execute() method…

  8. Stan Berka says:

    I love you well-written article and the idea, but I also have a few comments:
    1. It seems to me, this approach puts even more logic into the presentation layer, which is very hard to unit test.

    2. I’d say, that your approach should be used only for cases where the databinding mechanism is not applicable. Databinding is cleaner. Don’t you think?

    • thomasb says:

      Databinding is certainly cleaner than callbacks. However, if you have a single view/component instance that wants specific data (without databinding triggers to other instances), then callbacks is probably the cleanest solution. Remember that logic in presentation models works great. Heavy-loading logic in presentation components is not recommended. Instead create facade or delegate classes to manage complexities of multiple view interactions, etc.

  9. Stan Berka says:

    Any chance for a complete code sample for one of your examples, esp. the last one? Including the additional Cairngorm classes and those that need to be extended and answering to your three last items:

    (a) use a responder proxy wrapper class,
    (b) package the responder instance inside the event, and
    (c) extend the command base class to support the caching of the callback/view responder.

  10. pyso says:

    Graet and interesting article. Where can I find you r other secret tips #2 and #1? I cannot find them.

    Thanks and keep blogging!

    • thomasb says:

      Thanks pyso,
      As with all good intentions, reality often delays my blogging or responses.
      Secrets #1 and #2 are postponed… but I am two upcoming blog entries that you will LOVE.

  11. Roberto says:

    Great article,

    I’m not able to transfer the command result back to the view by responder. could you explain it?

    Can you share a sample project?
    It could help me to understand view notifictions.

    • thomasb says:

      Remember that your command MUST call super.execute(event) in order to cache any callbacks to views.
      e.g.
      override public function execute(event:CairngormEvent):void {
      super.execute(event);

      switch(event.type) {

      }
      }

      AND your response handlers must in turn always invoke a notifyCaller() to notify view or other originating callers that the synch or asynch event has responded/finished.
      e.g.

      notifyCaller();

  12. Kev says:

    “dependency injectors”

    Really Cool!

    Any idea when youre going to realese this feature?

    • thomasb says:

      Note that dependency injectors are already available in the Mate MVC. And… best of all, you can use the Mate EventMaps with Cairngorm. So the Mate EventMap will only have dependency injectors [MateDI] (no event handlers) AND Cairngorm has the event handlers. This is an excellent blend because MateDI supports injection into any specified class instance regardless of position in the view hierarchy. And whenever the data/model changes, all instances of the views get “re-injected”. So your Cairngorm (CGX) code modifies data models and presentation models, MateDI updates the views. By the way, such techniques also removes most needs for CGX callback or view notifiers.

      Stay tuned for a blog about LocalizationMaps and Cairngorm… for super-easy techniques and solutions to provide i18n/localization support to your apps WITHOUT ANY CHANGES to your view/UI components!

  13. Steven Kury says:

    Mike,

    I like this article!

    It was quite timely for me, as I have been working around the issue with a timer that polls the modelLocator for the desired info. (I’ve been activating the timer when calling the event and deactivating it in its listener when the changed info is received.) This is definitely a more elegant way to accomplish this goal. I’d like to use this solution instead.

    One question though, when you say “extend the command base class to support the caching of the callback/view responder,” which class are you proposing we use as the super class for the command base class? This wasn’t evident to me.

    • thomasb says:

      Steven,
      Never use timer’s to poll the ModelLocator :-) If you want to watch [from your views] for changes in the ModelLocator you can use (a) databindings, (b) ChangeWatchers, or (c) view notifications. Technique (b) allows you the ability to easily run your own custom code when the target data changes. Technique (c) can be used simultaneously with (a) and (b) to provide for very powerful systems of interactions.

  14. Moritz says:

    Hi Thomas,

    by talking about Cairngorm…

    I just have seen your presentation for the CairgormExtensions…(The adobe.acrobat.com One)

    Are you planning to do another one…. ;) ?

    really interessting stuff by the way… but I got still some problems I cannot get my head around…

    • thomasb says:

      Moritz,
      ViewNotifications are definitely challenging to wrap one’s head around. Just think about event bubbling or plate stacking; but here the bubbling is “up view to command, command to delegate, delegate to service and then back down to the view.” I think I will post 2 quick examples of view notifications: one without Cairngorm Extensions [CGX] and one with CGX.

      Meanwhile, I am planning on 3 more Cairngorm postings… so stay tuned.

      • Moritz says:

        oh that sounds great…

        By the way I spend a bit time in the past couple of days with studying the concept of notifying the view directly from the command via responder

        … Watching your docu / presentation carefully about 5 times ;) … together with your pdf and some example scripts helped me a lot… guess I would say I understand the process now… I just had to loose the idea of giving the model to much importance.

        There are just some bits… one mature thing which makes myself some headaches is the (creation) / use of modules withing in cgx’s. I love the new subController idea, this is so much better… But how would I create the module itself .. or lets say … more important where? – Would I create a command that deals with the whole process or would the view itself create the new module… but what happens if this new “miniApp/cgEnvironment” must be able to be created from anywhere in the project? In this case also a responder cannot help… isnt it?

        I think you mentioned something about Mate’s techniques in such a scenario.

        Is there anything you could advice on? Especially in context with modules in popups?

        Mercy in advance

  15. ergo says:

    it is useful to see how Command looks now.

    as for me it’s convenient to group events into common class. so when dispathing it we do:

    var event : AppEvent = new AppEvent (
    AppEvent.GET_USER_DETAILS_BY_SSN);
    event.responder = responder;
    event.ssn = “123″;
    event.password = “******”;
    event.dispatch();

    may be ssn and password better to srore in model (unless this is a very special password, just for this query only).

    so command will fetch its data from model vars:
    model.selectedSSN
    model.password

    • thomasb says:

      If your event also has properties or attributes with specific data types, then grouping events into a common class is not recommend. Consider the event AppEvent.GET_USER_DETAILS_BY_SSN which requires the SSN and password. But if the event AppEvent.UPDATE_USER wants a UserVO? Now your grouped event class has some attributes only used sometimes. This is as bad coding practice.

      And consider the issue of maintenance. Users will see an AppEvent used in your code, but they must determine the event type value in order to know why that event is being used. And what if the developer specifies the wrong event type?

      This is why unique, custom event classes are almost always recommended. Thus, I would create two event classes: GetUserDetailsEvent and UpdateUserEvent… and better yet, the class names even help with documentation and usage context.

  16. Wael Jammal says:

    Bojinx implements a system like this but it’s easier to use and provides more response types for each command.

    There is one restriction though that a few people have asked me to resolve and that’s the fact that they cant listen to the same response from multiple locations so I’m working on that now, in the next revision of Bojinx you should be able to use a responder like you do now for efficiency or listen to response events for a particular command from multiple locations.

    The solution works well though :) good to see other people implementing it.

    Wael Jammal’s last blog post..Bojinx Core V3.2.1 – Minor bug fix

  17. Great article Thomas! We need to get drinks and continue our “compare and contrast Cairngorm and Rails patterns” discussion. I recently had to solve a similar issue in my “as3-on-rails” stack, and I think you’d enjoy reviewing the code, comparing the techniques. I’ll blog about it once I fly the coop. :)

    • thomasb says:

      Michael,
      I am working on improvements to CGX that will deprecate some of the use cases for View Notifications. Using a similar solution found in the Mate framework, I will configure dependency injectors to inject data (whenever the data models are changed) directly into view properties. This will be very powerful.

      And I still need to post a blog about 3 Methods for Component Couplings in Flex. You will love that one also.

      Regarding drinks, as long as it is wine…. then anytime my friend. :-)

      • Prasanta Saha says:

        Hey this has helped me a lot. Thank you very much. I’ll be waiting for your “3 Methods for Component Couplings in Flex” article. [:)]

Leave a Reply

GD Star Rating
loading...

Please leave these two fields as-is:

Protected by Invisible Defender. Showed 403 to 1,090 bad guys.