How do I make a Flex Component Private?
Over on FlexCoders, Nate asked how he makes a Flex MXML Component private. I'll quote the important part of his question:
I have custom MXML component (ResourcesTree) that has a Tree inside of it. I don't want the Tree to directly be available outside the ResourcesTree Component.
His topic makes things a bit confusing. You make variables or methods private so they can't be used outside of a class. A private class would be one that you can't create an instance of? There would be no purpose in that.
Nate really wants to look into making an internal class. Internal classes can be used only within the package (AKA Directory), but not from outside.
Of course, don't go thinking that internal is not the same as private. People can extend your classes within the same package and use that internal class. I believe that the internal class can even be extended if need be.
If you are distributing a Binary SWC and want to hide the fact that you have a class in there, you can use the ExcludeClass metadata tag. This is used more than you'd think in the Flex Framework code.
Of course none of that will help you if people decompile your SWF.





<mx:HBox>
<mx:Lable id="innerLabel" />
<mx:HBox>
Anybody using my HBox component has access to innerLabel because the MXML compiler makes that variable public. If I had created this in AS3 I could have declared innerLabel to be private. Sure, someone could get a reference to it be using getChildAt(0), but at least auto-complete wouldn't expose it.
You could be right. My interpretation of his question was that he had a SWC package with multiple classes, but only wanted to expose one of them.
[ResourcesTree.mxml]
<mx:Canvas xmlns:mx="..." width="400" height="300">
<mx:Script>
...
</mx:Script>
<mx:WebService details.... />
<mx:Tree id="_tree" change="{dispatchEvent(event)}"/>
</mx:Canvas>
Outside of my Resources tree, I don't want anyone to be able to access my tree component. However it's automatically compiled as public.
Say I want to add an event listener to the tree change event. I want to force people using my component to listen to the ResourcesTree and like Danny said... not the sub-component.
For example:
var myResources:ResourcesTree = new ResourcesTree();
myResources.addEventListener(Event.CHANGE, onResourceChange); // GOOD
myResources._tree.addEventListener(Event.CHANGE, onResourceChange); // BAD!
private function onResourceChange(event:Event):void
{
trace("yay captured");
}
You'll notice they avoid any MXML in the Flex components.
I would suggest logging a feature request, then posting the link back here so we can all stick a vote against it.
an 'accessor' tag would be a fine addition IMO.
http://bugs.adobe.com/jira/browse/SDK-16181
I do not think it is easy. Once you add a component to the stage, the user can always get at it using the getChild methods. Even if the variable (AKA Component) is private or internal, it is still exposed once added to the stage.
You could override them, to not return your private component; but I find it hard to believe that would be worth the effort. I'm not sure what the implications would be for this on the rest of the framework.
I'm not too terribly worried about the getChild methods. Obviously there will almost always be a way to work around this. The people I'm working with have all the source for the classes / components I'm writing.
This for me is more a matter of following good OOP practices. I'm working in a multiple Flex developer environment right now, and we're writing components for all the other devs to use. I'm seeing other devs come in and directly accessing the subcomponents, which ends up causing all sorts of different bugs.
There is always going to be a way for developers to write crappy code, but I don't like being an enabler. :D
I think adding an accessor tag would help with encapsulation of custom MXML based components. If someone is using a component I write and sees a public property, they're going to assume its safe to use it. I could move things around in a later release and hose anything they've based on my component. If I declare them private then its safe to assume noone is supposed to be monkeying with it, and if they use the getChild to grab it then I'm free to hose them in any way I see fit :-)
This doesn't only apply to items added to a displayList either. You can create more than just DisplayObjects with MXML, and these should have the ability to be truly private.
I think the best way to address that from a code stand point is to switch to ActionScript classes, and make the appropriate internals private variables.
There may also be a business way to address it, but creating guidelines for your team on component building / reuse; or a more documented / better documented API.
Danny / Think,
I am not understanding what an assessor tag is, or what problem is solves in this case.
Right now when MXML is generated into Actionscript, a <mx:Tree> component's class is marked as public. It'd be nice be able to <mx:Tree accessor="private">, so when this block of MXML is generated into AS, it will use private instead of public.
How hard that would be to implement into the entire framework I'm not sure of.
I understand that writing these new components in AS is the way to go to get what I want. However it then defeats the purpose of why I'm using Flex in the first place, simplicity & consistency.
Much thanks! I understand what you mean by accessor now. I wonder the best way to approach it from a code perspective. You don't really want accessor as a property on the component, but more like a compiler directive somehow.
In many cases, I think that the Flex Framework offers great time savings over 'generic' ActionScript 3, but I'm not sure that MXML offers significant time savings over AS3 w/ the Flex Framework.
[ExcludeTesting.mxml]
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:local="*" creationComplete="init();">
<mx:Script>
<![CDATA[
import mx.controls.Button;
private function init():void
{
var mybtn:Button = myComp.btn;
mybtn.label = "Wootage";
}
]]>
</mx:Script>
<local:MyComponent id="myComp" />
</mx:Application>
[MyComponent.mxml]
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300">
<mx:Metadata>
[Exclude(name="btn", kind="property")]
</mx:Metadata>
<mx:Button x="167.5" y="146" label="Click Me" id="btn" />
</mx:Canvas>
It still allows me to access the component, change properties on the component etc. So unfortunately this doesn't solve the dilemma :(
Exclude will just remove the property from the Flex Builder code completion feature. Users can still manually type in the property name to drill down to it.
I thought it was used primarily to hide parent properties within the subclass. For example a VBox excludes the direction property of the Box component.
Nate,
I've never seen the property used in MXML. When you compile your code, there is a compiler flag to save the ActionScript. You might be able to look at the generated ActionScript to see where that metadata ends up.
You could also try something like this [written in the browser and not tested]:
[MyComponent.mxml]
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"; width="400" height="300">
<mx:Script>
<![CDATA[
import mx.controls.Button;
private var btn : Button = new Button();
override protected function createChildren():void{
super.createChildren();
btn.x = 167.5
btn.y = 146
btn.label = "Click Me"
addChild(btn);
}
]]>
</mx:Script>
</mx:Canvas>
That's all and good with simple components like a button. But it gets a bit harder when I want to do things like constraint based layout. When working with a Tree in actionscript I can't easily set the width and height to "100%" like I can with MXML. Which makes constraint based layout a much harder thing to achieve.
It's the small things like that, that make me want accessors in MXML.
Look into percentWidth and percentHeight.