I've found that having a "Back" button inside a component can be useful, but if the back button is hidden inside the component, it can't access the ViewStack (since it's in the parent).
I thought it would be useful to be able to pass a little bit of ActionScript to the component that would jump back to the previous view. It would effectively inject functionality from the parent to the child. Here's what my component looks like:
<?xml version="1.0" encoding="utf-8" ?>As you can see, this component allows you to define some text to display and the back button's click handler. The click handler has to be a Function object.
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%">
<mx:Script>
<![CDATA[
[Bindable] private var _labelText:String;
[Bindable] private var _buttonClick:Function;
public function set labelText(str:String):void {
_labelText = str;
}
public function set buttonClick(func:Function):void {
_buttonClick = func;
}
]]>
</mx:Script>
<mx:Label text="{_labelText}" />
<mx:Button label="Back" click="{_buttonClick()}" />
</mx:VBox>
Note that when you call this Function object, you have to put () at the end. It thinks this is actually a method that it can call, so you have to call it like any other method.
But when you instantiate this component, you can't just do something simple like buttonClick="viewStack.selectedChild=anotherView" ... while that may be the ActionScript code you want to execute, and it may work when you're defining the actual click event, the component will interpret this as a String instead of a Function, and it won't work. So you actually have to create a Function object in the parent.
<?xml version="1.0" encoding="utf-8" ?>Here, we create an anonymous method and assign it to the Function object, and then we can pass it to the component when we instantiate it.
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:components="components.*"
width="100%"
height="100%">
<mx:Script>
<![CDATA[
[Bindable] private var backToViewZero:Function =
function():void { viewStack.selectedChild = viewZero };
]]>
</mx:Script>
<mx:ViewStack id="viewStack" width="100%" height="100%">
<mx:VBox id="viewZero" width="100%" height="100%">
<mx:Label text="Start here!" />
<mx:Button label="One" click="viewStack.selectedChild=viewOne" />
<mx:Button label="Two" click="viewStack.selectedChild=viewTwo" />
</mx:VBox>
<components:MyBox id="viewOne" labelText="One" buttonClick="{backToViewZero}" />
<components:MyBox id="viewTwo" labelText="Two" buttonClick="{backToViewZero}" />
</mx:ViewStack>
</mx:Application>
This works splendidly, and is a pretty cool way to inject functionality down into your components that wouldn't otherwise be possible. (The component doesn't have to reach up to the parent somehow, which means your component doesn't need to know what the ViewStack is called, or about any of the other views; instead, it just needs to know that it's going to do something, and the parent is responsible for knowing what.)
It leaves me wondering, though, if there's a way to get around the problem of passing in code and having it interpreted as a String rather than the preferred Function. Perhaps by having the buttonClick setter take a String and create the Function object at that point, using something like eval(). Unfortunately, ActionScript 3.0 doesn't have eval(), so I don't know if this is possible. Maybe I'll figure something out later. In the mean time, I'll be creating Function objects in the parent.