5 Decoupled Bindings

Introduced with version 2.3 this feature adds a major new option to the existing ways of decoupling the parts of your application, injection and messaging. It's not a new idea though. Similar concepts existed in Seam in the Java world (based on its @In and @Out annotations) and have already been picked up by other Flex frameworks, too, like in GraniteDS that also offers [In] and [Out] metadata configuration.

5.1 Comparing Dependency Injection and Decoupled Bindings

This section aims to give an overview over the differences between two framework features that share a few similarities and a few suggestions for how to determine which mechanism to use in a particular use case.

Note that there are other frameworks who even mix these two mechanisms into a single feature. However I think that it is better to have a clear distinction as there are significant differences in the pros and cons for both. It also makes the code easier to read when there are distinct tags for these capabilities.

Dependency Injection

For details see 4 Dependency Injection

Classic Dependency Injection (using Parsley's [Inject] tag) offers the following advantages over Decoupled Bindings:

Decoupled Bindings

Decoupled Binding (using Parsley's [Publish] and [Subscribe] tags) offers the following advantages over Dependency Injection:

Of course there is also a price to pay compared to injection:

5.2 Basic Usage

To set up a publisher on a managed object you can use the Publish metadata tag:

[Publish][Bindable]
public var selectedContact:Contact;

In Flex applications you also need to use the [Bindable] tag on the publishing side, as the implementation for Flex is based on the standard Flex Binding architecture. For Flash applications see the last section in this chapter.

As soon as you update such a publisher property, the value will be pushed to all matching subscribers in any other object in any Context:

[Subscribe]
public var selectedContact:Contact;

If you do not specify an object id, the matching subscribers will be determined by the type of the property, in this case Contact. Like with injection and messaging this works polymorphically. You can publish a Dog and a subscriber for Animal would also get updated.

Like with all other features it is best to stick with matching by type and avoid the use of string identifiers if possible. If you need to publish multiple objects of the same type, you can specify an id:

[Publish(objectId="selectedContact")][Bindable]
public var selectedContact:Contact;

Of course the subscribers must specify the corresponding id then, too. In some case you may also be able to use scopes to confine the area of the application an object is published to and avoid the use of an id this way.

You can also let a property act as a publisher and subscriber at the same time:

[PublishSubscribe][Bindable]
public var selectedContact:Contact;

Now the value will be updated when any other publisher updates, but still act as a publisher itself.

Finally, like most other tags these tags can be used in MXML and XML, too:

<Object type="{MyPublisher}">
    <Publish property="selectedContact"/>
    <!-- other tags -->
</Object>

5.3 Avoiding Conflicts

For the Decoupled Binding facility to work in a reliable and robust way, you have to make sure that your setup does not lead to ambuigities. The framework must know which publisher is "in charge" for a particular subscriber at any point in time. In general publishing to different scopes cannot lead to conflicts as the scopes are completely shielded from each other. But for any publishing within a single scope for the same type or id (depending on whether you use an id), the rules are as follows:

As you see having more than one [Publish] tag for the same type or id within a single scope is illegal. The same goes for combining [Publish] and [PublishSubscribe] for the same type or id. In those cases the framework would not be able to determine "who is in charge". This problem does not occur with multiple [PublishSubscribe] tags. Since those are subscribers, too, changing one of them will also update all the others, so that they are always in sync.

5.4 Using Scopes

Like with Messaging the Decoupled Binding facility allows to use scopes. Sometimes you don't want to publish an object globally, but instead use different instances in different areas of the application. For a general introduction see 6.12 Using Scopes in the messaging chapter.

In contrast to messaging there is one major difference in the default behaviour. With messaging the default for the sending side is to dispatch through all scopes available for the Context so that the receiving side can decide which scope to listen for. This is different for publishing as the former would lead to conflicts too often (see preceding section). Instead the default is to publish only to the global scope.

For any other scope than the global scope you have to use the scope attribute explicitly:

[Publish(scope="window")][Bindable]
public var selectedContact:Contact;

And of course on all subscribers, too:

[Subscribe(scope="window")]
public var selectedContact:Contact;

5.5 Publishing Managed Objects

In all preceding examples the state of the published object did not change. You can use either container-managed objects or any other objects not known by the container and publish it to subscribers. If it is unmanaged it would remain so. In some cases you might want to explicitly add the object to the Context dynamically only for the time it is being published. This way it can also participate in messaging and other container features. This has the same effect as using the Context API to create dynamic objects like demonstrated in 7.7 Dynamic Objects, just with the advantage that you do not have to talk to the framework API.

This is how it works, you just add the managed attribute to the Publish tag:

[Publish(managed="true")][Bindable]
public var selectedContact:Contact;

Now anytime you set this variable to a Contact instance, that instance gets added to the Context automatically. It remains managed until:

This also means that the lifecycle methods demarcated with [Init] and [Destroy] can be used on the published object to get notified when the object gets published and removed.

5.6 Bindings in Flash

The implementation for Flash avoids the use of the Flex Binding facility. This feature is fully functional without Flex, too, but it requires some additional work. In Flash applications you would leave out the [Bindable] tag. Instead the publisher has to manually signal that the value of the publisher property has changed:

private var _contact:Contact;

[Publish]
public function get selectedContact () : Contact {
    return _contact;
}

public function set selectedContact (value:Contact) : void {
    _contact = value;
    dispatchEvent(new Event(Event.CHANGE));
}

If a single class has multiple publisher properties you can alternatively optimize and specify different change event types for different properties:

private var _contact:Contact;

[Publish(changeEvent="contactChanged")]
public function get selectedContact () : Contact {
    return _contact;
}

public function set selectedContact (value:Contact) : void {
    _contact = value;
    dispatchEvent(new Event("contactChanged"));
}

Apart from the necessity to dispatch a change event, everything else described in the preceding sections of this chapter also applies to Flash applications.