Binding Dependencies

4

Category : binding, binding dependency, Flex, parsley, presentation model

A common problem I’ve run into, even on the SDK team, was when you have one property dependent on another property (or a set of other properties), and you need that property to be Bindable. For example, in spark.components.VideoPlayer, volume is dependent on the videoDisplay.volume property. Usually to make such a property bindable, you add an event listener to know when your dependent property changes. In this case, if you check out VideoPlayer.partAdded(), you can see code like that. However, when I started using the presentation model pattern, this issue seemed to keep coming up quite frequently.

Let’s take a simple example where someone is placing an order. If that person has a billing address and a shipping address in California, we need to show them some extra questions because of extra regulations in California.

Our made up model looks like:

public class Order { 
 
    [Bindable]
    public var billingState:String;
 
    [Bindable]
    public var shippingState:String;
 
}

And in the View, we want to do something like:

<s:Form>
    <s:FormItem label="Billing State">
        <s:DropDownList selectedItem="@{ presentationModel.order.billingState }">
            <s:ArrayList>
                <fx:String>AL</fx:String>
                <fx:String>AS</fx:String>
                <fx:String>AZ</fx:String>
                <fx:String>AR</fx:String>
                <fx:String>CA</fx:String>
                <fx:String>CO</fx:String>
                <fx:String>CT</fx:String>
                <fx:String>DE</fx:String>
            </s:ArrayList>
        </s:DropDownList>
    </s:FormItem>
 
    <s:FormItem label="Shipping State">
        <s:DropDownList selectedItem="@{ presentationModel.order.shippingState }">
            <s:ArrayList>
                <fx:String>AL</fx:String>
                <fx:String>AS</fx:String>
                <fx:String>AZ</fx:String>
                <fx:String>AR</fx:String>
                <fx:String>CA</fx:String>
                <fx:String>CO</fx:String>
                <fx:String>CT</fx:String>
                <fx:String>DE</fx:String>
            </s:ArrayList>
        </s:DropDownList>
    </s:FormItem>
 
    <s:FormItem label="California Question1"
                includeInLayout="{ presentationModel.order.shippingState == 'CA' &amp;&amp; presentationModel.order.billingState == 'CA' }"
                visible="{ presentationModel.order.shippingState == 'CA' &amp;&amp; presentationModel.order.billingState == 'CA' }">
        <s:TextInput />
    </s:FormItem>
 
    <s:FormItem label="California Question2"
                includeInLayout="{ presentationModel.order.shippingState == 'CA' &amp;&amp; presentationModel.order.billingState == 'CA' }"
                visible="{ presentationModel.order.shippingState == 'CA' &amp;&amp; presentationModel.order.billingState == 'CA' }">
        <s:TextInput />
    </s:FormItem>
</s:Form>

Now the same code: presentationModel.order.shippingState == ‘CA’ &amp;&amp; presentationModel.order.billingState == ‘CA’ is repeated 4 times, the ampersands have to be escaped, and overall it’s just quite ugly. Now, one of the neat tricks I’ve learned is that you can spruce it up by doing something like:

<fx:Declarations>
    <fx:Boolean id="showCaliforniaQuestions"> {presentationModel.order.shippingState == "CA" &amp;&amp; presentationModel.order.billingState == "CA"} </fx:Boolean>
</fx:Declarations>
 
...
 
<s:FormItem label="California Question1" includeInLayout="{ showCaliforniaQuestions }" visible="{ showCaliforniaQuestions }">
    <s:TextInput /> 
</s:FormItem>
<s:FormItem label="California Question2" includeInLayout="{ showCaliforniaQuestions }" visible="{ showCaliforniaQuestions }">
    <s:TextInput /> 
</s:FormItem>

That definitely works and is much cleaner than before; however, that means we have some business logic in our View that is extremely hard to test. That logic should really be in the presentation model. If we try to move it into the Presentation Model, we’ll end up with something that looks like:

<s:FormItem label="California Question1" 
    includeInLayout="{ presentationModel.showCaliforniaQuestions }" 
    visible="{ presentationModel.showCaliforniaQuestions }">
    <s:TextInput /> 
</s:FormItem> 
<s:FormItem label="California Question2" 
    includeInLayout="{ presentationModel.showCaliforniaQuestions }" 
    visible="{ presentationModel.showCaliforniaQuestions }">
    <s:TextInput />
</s:FormItem>
public class MyPM {
    public function MyPM() {
        super();
    }
 
    [Bindable]
    public var order:Order = new Order();
 
    public function get showCaliforniaQuestions():Boolean 
    {
        return order.shippingState == "CA" && order.billingState == "CA";
    }
}

However, doing that doesn’t work. The reason is because showCaliforniaQuestions isn’t Bindable. Now, to make it Bindable, we need to add Bindable metadata on top, add event listeners (or use a ChangeWatcher) to let us know when the shipping state or the billing state changes, and dispatch a new binding event when we get notified that the shipping or billing state changes. This turns out to be quite a lot of extra, ugly code, which means most people just end up keeping this logic in the View because practically it’s just too much work and too ugly to move it to the Presentation Model. This is all fine, but this kept issue kept cropping up in practice, so I finally sat down to come up with a more palatable option.

One of the very cool things about Parsley is that you can add your own custom Metadata, and Parsley will help process it for you. Even though I’m really new to Parsley, it turns out that this is relatively easy to do. So I decided to go ahead and add some custom metadata to help deal with this. What’s really neat is that I added a Parsley processor for Bindable metadata. I added a new property on that metadata, which allows you to define binding dependencies–basically Bindings that are dependent on other properties. In our particular example, here’s what it looks like:

[Bindable(event="showCaliforniaQuestionsChanged",dependentProperty="order.shippingState")]
[Bindable(event="showCaliforniaQuestionsChanged",dependentProperty="order.billingState")]
public function get showCaliforniaQuestions():Boolean
{
    return order.shippingState == "CA" && order.billingState == "CA";
}

All we did was declare the showCaliforniaQuestions as Bindable, where that binding is dependent on the order.shippingState and order.billingState properties. The Parsley metadata processor will step in and handle everything else. I personally like this solution a lot as it piggy-backs off of the Flex SDK framework’s Bindable metadata and just extends it for our purpose. It’s easy to use and pushes our logic into Presentation Model, which makes our code cleaner and more importantly, testable.

The main downside to this approach is that we’re exposed to spelling mistakes or later refactorings since the dependentProperty in the Bindable metadata is not checked by the compiler. Ideally, this would be something we could add to the ActionScript compiler so that it could inspect the getter and pick out all the bindable variables. However, we don’t have that, and I find this pattern really convinient for me. The code for all of this can be found in the attached Flash Builder project — feel free to use it.

Comments (4)

I’ve found no need for the presentation model since the introduction of spark the architecture. This provides a clean separation of View from controlling logic, and could take your above example one step further…

Instead of using a binding in your skin for those FormItems you could either create states that they get included/excluded in, or just have your controller class include/exclude them. This leaves you with a much more vanilla view where there person creating it doesn’t have to have any knowledge of logic and methods on the controller class.

So why use a PresentationModel over this spark approach?

Hey Tink,

I’ve really been meaning to head to the London user group meetings, and the one this week on byteCode sounds really interesting, but unfortunately, I’ll be heading out of town. Soon, we’ll get to meet up in this city :-)

I think there are a lot of ways to accomplish the same task. I love the Spark skinning model, and you could definitely use it here. There are a lot of similarities between the Presentation Model pattern and the Spark skinning pattern; however, I view them as used for different purposes: The SkinnableComponent/Skin contract is for making a component’s visuals be changeable, while the PresentationModel pattern is for pushing in business logic into the component.

The main reasons I wouldn’t use the Spark Skinning architecture to accomplish the same thing as the Presentation Model pattern is because 1) I don’t want the business logic tied to a UIComponent because it’s lighter-weight and more easily testable without that tie (and when using Parsley, this is almost required for performance reasons since it can end up looping of all of the properties on your presentation model class), 2) Sometimes the abstraction I want for a component requires a Skinnable architecture (especially with SkinnableContainers)–would you just extend the SkinnableContainer and add in the PM business logc? If so, I feel like changing the View (the UI component you’re using) now requires you to move your business logic around.

I like the Spark approach because it’s easier for the component and the skin to talk to each other through their well-defined skinning contract. I was thinking of bringing a similar approach to Presentation Models. I don’t see anything wrong with having the model expect certain APIs from the View and using something like Skin states or Skin parts to do this. However, I haven’t played around with it yet to see exactly how it feels.

Tink,

This is one way to use the Spark architecture indeed. I personally wouldn’t use it like that though. You quickly reach the limitation of the state mechanism if you start using these to model application states; Indeed, states are just a bunch of concatenated strings and it quickly goes out of hand (See VideoPlayerSkin for a example, although it could get much worse). That’s why I would leave states to primarily model control/component states.

In spark, the skinning concern have been moved to the skin but the component itself still have a lot of event handling, synchronizing, logic left in it. The kind of logic you would never find in a PM, and that’s a good thing. Further separation of concerns.

PMs have many advantages over skin states too. If you write your whole application using SkinnableComponents, you end with way too much (AS3) code (partAdded, partRemoved methods quickly get big); While this is fine for reusable components, it’s way too much work and error prone for fast changing application views (a.k.a MXML views); Instead those views should leverage bindings and inline event handling to be assembled quickly and be as concise as possible. Bindings swallow null pointer errors and observing the PM is just extremely simple (As opposed to the opposite approach: The mediator like in PureMVC, a very bad fit for a Flex app).

There are other reasons why PMs are used in some contexts (For instance, it plays well with IoC) but I think this is already well documented.

I would use SkinnableComponent/Container only when there is a genuine need for the flexibility of skin swapping OR if the skinning is complex enough (For instance, not just one Rect) that it would be best to separate it and let someone good with pixels work on that file in isolation. For everything else, MXML views are pure win and are the main reason Flex is way better than Flash for enterprise development: You quickly get an impressive result, declaratively.

Neat solution, however you just replaced one dependancy (display list) for another, namely parsley. In order to properly test your business logic in this case you will have to hook up parsley processors to do the boilerplate work, otherwise your logic is broken. Not necessarily difficult but it can be a tricky dependency to spot, especially for someone who’s not you but inherits the code :)

Still, much neater than keeping the logic in the view.

Post a comment