I have a component with two Pie Charts that display percentages at two specific dates (think start and end values). But, I have three views: Start Value only, End Value only, or show Both. I am using a ToggleButtonBar to control the display. What is the best practice for changing this kind of view state? Right now (since this code was inherited), the view states are changed in an ActionScript function which sets the visible and includeInLayout properties on each Pie Chart based on the selectedIndex of the ToggleButtonBar, but, this just doesn't seem like the best way to do this - not very dynamic. I'd like to be able to change the state based on the name of the selectedItem, in case the order of the ToggleButtons changes, and since I am storing the name of the selectedItem for future reference.
Would using States be better? If so, what would be the best way to implement this?
Thanks.
Current logic:
private function pieTypeToggleButtonBar_itemClickHandler():void
{
// Show/Hide the appropriate Pie Charts based on the user's selection
switch (pieTypeToggleButtonBar.selectedIndex)
{
// "Start Value" is selected
case 0:
{
// Hide the End Value Pie Chart
endValuePieChartVBox.visible = false;
endValuePieChartVBox.includeInLayout = false;
// Show the Start Value Pie Chart
startValuePieChartVBox.includeInLayout = true;
startValuePieChartVBox.visible = true;
break;
}
// "End Value" is selected
case 1:
{
// Hide the Start Value Pie Chart
startValuePieChartVBox.visible = false;
startValuePieChartVBox.includeInLayout = false;
// Show the End Value Pie Chart
endValuePieChartVBox.includeInLayout = true;
endValuePieChartVBox.visible = true;
break;
}
// "Both" is selected
case 2:
{
// Show the Start Value Pie Chart
startValuePieChartVBox.includeInLayout = true;
startValuePieChartVBox.visible = true;
// Show the End Value Pie Chart
endValuePieChartVBox.includeInLayout = true;
endValuePieChartVBox.visible = true;
break;
}
}
}
<mx:ToggleButtonBar id="pieTypeToggleButtonBar" selectedIndex="1"
itemClick="pieTypeToggleButtonBar_itemClickHandler()">
<mx:Array>
<mx:Object name="startValue" label="Start Value" />
<mx:Object name="endValue" label="End Value" />
<mx:Object name="both" label="Both" />
</mx:Array>
</mx:ToggleButtonBar>
Since the currentState property takes a String, which maps to the name property of a state, then it sounds like using <mx:states> would work well in your case. In fact I use states often for toggling between views in just the way you describe -- setting visible and includeInLayout properties of components (usually Canvas components, or other sorts of containers) with SetProperty:
<mx:states>
<mx:State name="View State 1">
<mx:SetProperty target="{component1}" name="visible" value="true" />
<mx:SetProperty target="{component2}" name="visible" value="false" />
<mx:SetProperty target="{component3}" name="visible" value="false" />
<mx:SetProperty target="{component1}" name="includeInLayout" value="true" />
<mx:SetProperty target="{component2}" name="includeInLayout" value="false" />
<mx:SetProperty target="{component3}" name="includeInLayout" value="false" />
</mx:State>
<mx:State name="View State 2">
<mx:SetProperty target="{component1}" name="visible" value="false" />
<mx:SetProperty target="{component2}" name="visible" value="true" />
<mx:SetProperty target="{component3}" name="visible" value="false" />
<mx:SetProperty target="{component1}" name="includeInLayout" value="false" />
<mx:SetProperty target="{component2}" name="includeInLayout" value="true" />
<mx:SetProperty target="{component3}" name="includeInLayout" value="false" />
</mx:State>
<mx:State name="View State 3">
<mx:SetProperty target="{component1}" name="visible" value="false" />
<mx:SetProperty target="{component2}" name="visible" value="false" />
<mx:SetProperty target="{component3}" name="visible" value="true" />
<mx:SetProperty target="{component1}" name="includeInLayout" value="false" />
<mx:SetProperty target="{component2}" name="includeInLayout" value="false" />
<mx:SetProperty target="{component3}" name="includeInLayout" value="true" />
</mx:State>
</mx:states>
<mx:Script>
<![CDATA[
import mx.binding.utils.BindingUtils;
import mx.binding.utils.ChangeWatcher;
private function this_creationComplete(event:Event):void
{
// Use BindingUtils.bindSetter to hook into selectedIndex-change events
var cw:ChangeWatcher = BindingUtils.bindSetter(setState, myBar, "selectedIndex");
}
private function setState(index:int):void
{
currentState = myBar.getChildren()[index].label;
}
]]>
</mx:Script>
<mx:ToggleButtonBar id="myBar">
<mx:dataProvider>
<mx:Array>
<mx:String>View State 1</mx:String>
<mx:String>View State 2</mx:String>
<mx:String>View State 3</mx:String>
</mx:Array>
</mx:dataProvider>
</mx:ToggleButtonBar>
<mx:Canvas id="component1">
<mx:Text text="Component 1" />
</mx:Canvas>
<mx:Canvas id="component2">
<mx:Text text="Component 2" />
</mx:Canvas>
<mx:Canvas id="component3">
<mx:Text text="Component 3" />
</mx:Canvas>
... following this general pattern. Hope it helps!
The simplest way to do this would be to use the ViewStack component. That way you just select the selectedIndex and all the other panels will hide (watch out for initialization problems with ViewStacks).
Because of the problems i have had with ViewStacks in the past i would probably be inclined to use view states though as an alternative. States have their own problems but they are definitely feasible for this particular problem.
If i were you i would look into either of these options as a solution as the functionality created about has already been created with a standard api.... try and stick to using mx components if they fit your specific needs rather than reinventing the wheel all the time
The use of states in the first component completely misses the point. You shouldn't be setting visible and includeInLayout, you should be ADDING and REMOVING the components using AddChild and RemoveChild. The use of visible and includeInLayout to control object's state on the screen is a bastardization of their intent. Unfortunately, since Flex doesn't have any sort of conditional logic tags or even conditional attributes to add/remove elements, people often fall back on those two tags. Sometimes that's the only practical thing to do, but in your case or in any case where you have a 'switch' statement, you definitely want to use states.
Related
I'm interested in finding out the best approach to this issue, it's not technically difficult but there must be an elegant solution.
Basically i have a form that features mostly text inputs, i would like to change the style of the input boxes based on the current state.
I can do this in the mxml on each input...
<s:TextInput text="label" borderColor.State1="0xFFFFFF" borderColor.State2="0x000000"/>
But that involves creating properties on every single item in the form.
There must be a better way of doing this without adding a property to each item?
Thanks!
You can use transitions and SetAction to set styles on multiple objects at the same time based on a new viewstate. This is a piece of an example from the SDK documentation:
.
<s:states>
<s:State name="Login" />
<s:State name="Register" />
</s:states>
<s:transitions>
<!-- Define the transition from the base state to the Register state.-->
<s:Transition id="toRegister" fromState="*" toState="Register">
<s:Sequence targets="{[loginPanel, registerLink, confirm, loginLink]}">
<s:RemoveAction />
<s:Fade />
<s:SetAction target="{loginPanel}" property="title" />
<s:SetAction target="{loginButton}" property="label" />
<s:SetAction target="{loginButton}" property="color" />
<s:Resize target="{loginPanel}"/>
<s:AddAction />
<s:Fade />
</s:Sequence>
</s:Transition>
<!-- Define the transition from the Register state to the base state.-->
<s:Transition id="toDefault" fromState="Register" toState="*">
<s:Sequence targets="{[loginPanel, registerLink,
confirm, loginLink]}">
<s:RemoveAction/>
<s:SetAction target="{loginPanel}" property="title"/>
<s:SetAction target="{loginButton}" property="label"/>
<s:SetAction target="{loginButton}" property="color"/>
<s:Resize target="{loginPanel}"/>
<s:AddAction/>
</s:Sequence>
</s:Transition>
</s:transitions>
You can just target everything in the sequence (instead using different targets for each SetAction) and use the 'value' property of the SetAction to set the values to what you want.
I have a dataProvider that's defined with mx:XML like this.
<mx:dataProvider>
<mx:XML format="e4x">
<root label="All Items">
<morning label="Evening" type="check" />
<evening label="Evening" type="check" />
<night label="Night" type="check" />
</root>
</mx:XML>
</mx:dataProvider>
This doesn't seem to be a valid option anymore with the new spark architecture. Can someone suggest how this can be done? I thought if I put in Declarations, it would work, but doesn't seem to be the case.
Try:
<fx:XML>
...
</fx:XML>
How to validate the radio button is selected or not in flex 3?
if my question is wrong, please suggest me any thing regarding the validation of radio group.
Simply use a StringValidator:
<mx:StringValidator id="myRadioButtonGroupValidator"
source="{myRadioButtonGroup}"
property="selectedValue"
required="true"/>
For Spark groups and RadioButtons things work slightly different. See the example below.
Note: For a HGroup as the example shows: The warning-sight will appear for errors but there will be no red-colored border be visible. If you set a RadioButton itself as listener you might get an ugly result and if you set a FormItem as target you will see nothing happen.
<fx:Declarations>
<s:RadioButtonGroup id="myGroup" />
<mx:StringValidator id="vLevel"
required="true"
source="{myGroup}"
property="selectedValue"
minLength="1"
maxLength="80"
listener="{grpLevel}"
/>
</fx:Declarations>
<s:FormItem label="Level">
<s:HGroup id="grpLevel">
<s:RadioButton group="{myGroup}" label="A"/>
<s:RadioButton group="{myGroup}" label="B"/>
<s:RadioButton group="{myGroup}" label="C"/>
</s:HGroup>
</s:FormItem>
This is the way I solved the problem. If anything is wrong please leave a the comment.
<mx:NumberValidator id="radiogroupValidator" source="{radiogroup}" property="selectedValue" allowNegative="false" />
radio group source in mxml file
<mx:RadioButtonGroup id="radiogroup" itemClick="radiochangefunction(event)" selectedValue="-1" />
<mx:RadioButton id="radiobtn1" groupName="radiogroup" label="Send Password to existing EmailId" value="0"/>
<mx:RadioButton id="radiobtn2" groupName="radiogroup" label="Enter new EmailId" value="1"/>
The itemClick function
public function radiochangefunction(event):void
{
radiogroup.selectedValue=event.currentEvent.selectedValue.toString();
}
and finally in validation function
var isValidradiobutton:Boolean = (Validator.validateAll([radiogroupValidator]).length==0);
Listen to the itemClick event of the RadioButtonGroup. Within the handler, use selection or selectedValue property to know which RadioButton was clicked.
selection - returns a reference to the selected RadioButton instance
selectedValue - returns the value property of the selected RadioButton, if it is set. Otherwise, returns its label text.
Both of these properties return null if no RadioButton is selected.
A running example from livedocs
<?xml version="1.0"?>
<!-- Simple example to demonstrate RadioButtonGroup control. -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.ItemClickEvent;
// Event handler function to display the selected button
// in an Alert control.
private function handleCard(event:ItemClickEvent):void {
if (event.currentTarget.selectedValue == "AmEx") {
Alert.show("You selected American Express")
}
else {
if (event.currentTarget.selectedValue == "MC") {
Alert.show("You selected MasterCard")
}
else {
Alert.show("You selected Visa")
}
}
}
]]>
</mx:Script>
<mx:Panel title="RadioButtonGroup Control Example" height="75%" width="75%"
paddingTop="10" paddingLeft="10">
<mx:Label width="100%" color="blue"
text="Select a type of credit card."/>
<mx:RadioButtonGroup id="cardtype" itemClick="handleCard(event);"/>
<mx:RadioButton groupName="cardtype" id="americanExpress" value="AmEx"
label="American Express" width="150" />
<mx:RadioButton groupName="cardtype" id="masterCard" value="MC"
label="MasterCard" width="150" />
<mx:RadioButton groupName="cardtype" id="visa" value="Visa"
label="Visa" width="150" />
</mx:Panel>
</mx:Application>
I created my own validation control.
something like this
control.mxml:
<mx:states>
<mx:State name="ExceptionState">
<mx:AddChild relativeTo="{hbox1}">
<mx:TextArea id="txtError"
styleName="errorMessage"
width="140" minHeight="26"
wordWrap="true" editable="false"
/>
</mx:AddChild>
<mx:SetProperty name="height" value="56" />
<mx:SetProperty target="{txtInput}" name="y" />
<mx:SetProperty name="width" value="100%" />
</mx:State>
</mx:states>
<mx:HBox width="100%" x="0" y="0" id="hbox1" styleName="nopadding">
<mx:TextInput id="txtInput"
text="{text}"
displayAsPassword="{displayAsPassword}"
maxChars="{maxLength}"
cornerRadius="0"
height="22"
styleName="{inputStyleName}"
width="{_inputWidth}"
/>
</mx:HBox>
When I validate the txtinput and it's not valid, the textarea will appear with some information in it.
Now I have several textinput fields in a canvas
<custom:control id="eerstes" errormessage("message1");/>
<custom:control id="tweeds"errormessage("message2");/>
etc.
but when eerstes is not valid, the textarea appears and my custom control with id tweeds will shift a little.
Is it possible to let the textarea float but that is is still relative to hbox1?
Thank you,
JSMB
If I were you, I would try using the PopUpManager.addPopUp to place the TextField into the box. Once you've done that, you can manually reposition the TextField without effecting the alignment of the box's children. You may even be able to get this to work using BindingUtils, so that you don't have to reposition this every time.
On the other hand, you could use Alert.show, which would simply put a pop-up over the entire SWF.
What is the base class for your component custom:control ? Does it extend Canvas?
I have the following MXML:
<mx:State name="myState">
<mx:AddChild relativeTo="{myhbox}" position="after">
<mx:Box verticalAlign="middle" horizontalAlign="center" width="100%" height="100%">
<mx:Form id="myForm" width="479" verticalScrollPolicy="off" horizontalScrollPolicy="off">
<mx:FormItem label="My Label:" fontWeight="bold" id="myLabel" direction="vertical">
<mx:TextInput id="myTextInput" width="282" />
<mx:HBox>
<mx:Button label="Go" click="go();" id="goButton" />
</mx:HBox>
</mx:FormItem>
</mx:Form>
</mx:Box>
</mx:AddChild>
</mx:State>
How do I set focus on the TextInput field using <mx:SetProperty/>? I've tried the following, but it only results in the field being highlighted -- the cursor does not appear in the TextInput:
<mx:SetProperty target="{stage}" name="focus" value="{myTextInput}"/>
Long story short, I want the cursor to appear in the field.
UPDATE: I figured it out. See comments for solution.
I try to avoid using the AddChild state tag. It's usually better to put all that in a component, and use SetProperty to set visible and includeInLayout when you want it to display.
You can always override visible in your custom component to set the focus to the field. Or create a custom setter than does the same thing
public function set show(value:Boolean):void
{
visible = true;
includeInLayout = true;
if (value)
myFunctionThatSetsTheFocus();
}
Add a "creationComplete" to the TextInput and had it call a method that setFocus on the TextInput