Wednesday, July 23, 2008

Making MXML Subcomponent Private

Recently someone asked on the flexcoders mailing list how to make a subcomponent private when working in MXML. The short answer is that this cannot be done since MXML does not support access control settings: i.e. anything declared in MXML is public, and cannot be changed to private, protected or internal. However, things are not that simple.

Let's assume that we have the following setup:

MyComponent.mxml extends VBox (or any other container).
MyComponent has a TextInput MXML tag within it.
MyApplication.mxml has a MyComponent MXML tag within it.

Our goal is to have the TextInput component treated as a private property of MyComponent: i.e. it should be accessible within MyComponent, but not from MyApplication.

Solution I: Omit Id Attribute

The simplest way to achieve this would be to not have an id attribute for the TextInput control within MyComponent. If the TextInput control does not have an id, there will be no way for MyApplication to access it. (For purposes of this discussion, we are not worried about the fact that the TextInput control will be accessible as a child of MyComponent in the display list.)

The obvious drawback of this approach is that the TextInput control will also be inaccessible within MyComponent: i.e. it will be impossible to bind to properties of the TextInput control, or to use script to set or get any of the TextInput control's properties. In cases where that limitation is acceptable, omitting an id tag is an easy way to achieve privacy.

Solution II: Coding Convention

A second way to achieve rough equivalency with privacy is through coding conventions. For example, one could decide as an organization that any property that began with an underscore would be respected as private. So if in MyComponent we declared our TextInput like this
<mx:TextInput id="_myText" />

It would be considered inappropriate within MyApplication to have code such as the following:
myComponent._myText.text

Of course, within MyComponent, it would be considered appropriate to have code such as the following:
_myText.text

or
this._myText.text

With this approach, it is now possible within MyComponent to bind to the properties of _myText, as well as to get and set its properties. Although this approach has the obvious disadvantage that the compiler will not enforce it, it could go a long way toward achieving the desired end.

Solution III: Proxy

A more complicated, but more powerful solution is to use a proxy variable. In this approach, the TextInput control does not have an id attribute. Instead, it triggers an event which calls a function that assigns a reference to the TextInput control to a private variable. Within MyComponent, our code might look like this:

<mx:Script>
<![CDATA[
[Bindable]
private var textProxy:TextInput;

private function subDone(event:Event):void {
textProxy = event.target as TextInput;
}
]]>
</mx:Script>
<mx:TextInput creationComplete="subDone(event);" />

Within MyComponent, one can bind to properties of the TextInput control, as well as set or get its properties, by using the textProxy variable. For example, we could add a text control which will mirror what is typed in the TextInput control by adding the following code:
<mx:Text text="{textProxy.text}" />

In essence, we have assigned an id property to the TextInput control, but one that is private. Thus, within MyApplication, there is no way to access the TextInput control since it has no id, and since textProxy is private.

Solution IV: the [Exclude] Metadata Tag

See the blog post at http://blog.ashier.com/2008/03/25/hiding-properties-in-flex-components/, including my comment on the limits of this approach.

1 comment:

rishu said...

Thanks ... I was looking for this solution