Adding a child to a VBox component (FLEX) - apache-flex

First of all I wanted to thank in advance to everyone that reads this post.
I'm having a problem when adding a child to a VBox component. Is it not the same thing?:
List Item:
<mx:Script>
<![CDATA[
public function addChildren():void {
var f:FaqItem=new FaqItem();
f.id="newUsersAssistance";
this.cont.addChild(f);
}
]]>
</mx:Script>
<mx:VBox id="cont" width="100%" borderThickness="0" verticalGap="0"/>
and:
<mx:VBox id="cont" width="100%" borderThickness="0" verticalGap="0">
<view:FaqItem id="newUsersAssistance" />
</mx:VBox>
I am injecting two properties (question and answer) to the FaqItem component from an auxiliar file (using BabelFX) that depends on the id of the FaqItem, but it is only working when I choose the second option. If I use the first option, I get a child but with the text in question and answer fields is empty. I need to use the first option.
Is there anything I am doing wrong?
Thanks again for everything
Kind Regards

i don't think you will be able use the id property of the dynamically added component to do Injection. I suggest you keep some bindable variables to bind the value to the dynamic FaqItem.

Related

How to check the state of a Radio Button in Flex 3?

I'm working on a template for dynamic questionnaires in Flex. More detailed description in my previous thread HERE
To create the questionnaire, I use nested repeaters - one for the questions and one for the answers (as their amount may vary).
<mx:Repeater id="r" dataProvider="{questions}">
<mx:Label text="{r.currentItem.question}" width="200"/>
<mx:Repeater id="r2" dataProvider="{r.currentItem.answers}">
<mx:RadioButton label="{r2.currentItem.text}" width="200"
click="answerHandler(event, event.currentTarget.getRepeaterItem())"/>
</mx:Repeater>
</mx:Repeater>
To understand my data providers, it's probably best to check the answer for my previous thread - easier than if I try to explain it here.
The question here is... As you can see, I created click event handler for each radio button and my plan was to do playerScore++ every time the user chose correctly (which can be achieved by checking the Boolean property "correct" of sent RepeaterItem).
However, I see now that even if the button is selected already, I can still click on it more times, and even though it's not changing anything in the view, it increments the score every time.. I would also have to handle situation in which the user changes his mind (I could give + points for each good answer and - points for wrong, but this would mean, that if the user chose more wrong answers, my score will be negative and I don't want it).
So it would be way way easier to just have a Submit button and check the states of all my buttons and if they are correct only after the user clicks "submit". Is it possible?
I recommend you to refer to the following sample and add RadioButtonGroup to your repeater groups and listen to change events instead of click. You can listen change event right in RadioButtonGroup and check if (radioGroup.selectedValue.correct) for correctness of new selection. See corresponding documentation.
To have possibility to assign radiogroups unique name you have to extract inner repeater with answers into separate component. Breaking your application into smaller components can make your application more clear and readable. You can treat every MXML file as a class (as in OOP) but in declarative form. And to tell the true every MXML component is a class which inherited from root-node-class.
Lets look at the code.
First, our inner component which serves answers:
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Metadata>
[Event(name="rightAnswerEvent", type="AnswerEvent")]
</mx:Metadata>
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
public var answers:ArrayCollection;
protected function answerGroup_changeHandler(event:Event):void
{
if (answerGroup.selectedValue.correct)
dispatchEvent(new AnswerEvent(AnswerEvent.RIGHT_ANSWER_EVENT));
}
]]>
</mx:Script>
<mx:RadioButtonGroup change="answerGroup_changeHandler(event)" id="answerGroup" />
<mx:Repeater dataProvider="{answers}" id="answersRepeater">
<mx:RadioButton group="{answerGroup}" label="{answersRepeater.currentItem.text}"
value="{answersRepeater.currentItem}" />
</mx:Repeater>
</mx:VBox>
It gets answers collection as input and fires our custom event to inform some components about right answer (see Metadata tag).
Our custom event is pretty simple an looks like the following:
package
{
import flash.events.Event;
public class AnswerEvent extends Event
{
public static const RIGHT_ANSWER_EVENT:String = "rightAnswerEvent";
public function AnswerEvent(type:String)
{
super(type);
}
}
}
So now our top level component:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application layout="vertical" xmlns:local="*"
xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var questions:ArrayCollection = new ArrayCollection();
[Bindable]
private var scoreCounter:int;
]]>
</mx:Script>
<mx:Label text="{'Score ' + scoreCounter}" />
<mx:Repeater dataProvider="{questions}" id="questionRepeater">
<mx:Label text="{questionRepeater.currentItem.question}" />
<local:AnswerGroup answers="{questionRepeater.currentItem.answers}" rightAnswerEvent="scoreCounter++" />
</mx:Repeater>
</mx:Application>
I omitted initialization code to populate our Question and Answer domain objects with data from XML (see previous thread).
So now we have compact modularized code where every part solves its own task.
Hope this helps!

popup editor for datagrid items - hangs the browser

i've got a pretty straightforward thing: a datagrid which renders some items. clicking on an item would bring up a popup editor (as the item has lots of properties and may not be edited right in the datagrid).
the popup contains just a form and a [Bindable] reference to the item it edits (which is passed from itemClick handler of the datagrid). form's default values are taken by binding to corresponding item properties with {} notion, while form values are bound back to the item using mx:Binding tags.
and now the problem. when the popup is brought up for the first time, everything is fine. however, when after being closed the popup is brought up again by clicking on the same item, the browser hangs (afaik because of change watchers being endlessly fired resulting in stackoverflow or something similar).
we have same behaviour in Safari, IE and Chrome, so i guess it's not to do with something browser-related. removing either [Bindable] from the item reference in the popup or mx:Binding tags from editors suppresses the problem, but of course the editing no longer works.
i'm banging my head against the wall for several days already, but still can't make it work. does it ring a bell to someone, what can be wrong in here (what can be damn easier that this)?
here's the code of the popup:
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" title="Details"
showCloseButton="true" close="PopUpManager.removePopUp(this);" creationComplete="PopUpManager.centerPopUp(this)">
<mx:Script>
<![CDATA[
import mx.managers.PopUpManager;
import my.Detail;
[Bindable] private var _documentDetail:Detail;
public function set documentDetail(value:Detail):void {
this._documentDetail = value;
}
public function set readOnly(value:Boolean):void {
if (value) {
this.currentState = "read-only";
}
}
]]>
</mx:Script>
<mx:states>
<mx:State name="read-only">
<mx:SetProperty target="{startDate}" name="enabled" value="false"/>
<mx:SetProperty target="{comments}" name="enabled" value="false"/>
</mx:State>
</mx:states>
<!--
<mx:Binding source="this.startDate.selectedDate" destination="_documentDetail.startDate"/>
<mx:Binding source="this.comments.text" destination="_documentDetail.comment"/>
-->
<mx:VBox width="100%" height="100%">
<mx:FormItem label="{resourceManager.getString('eRequestAppli','startdate')}:" labelWidth="160" width="100%">
<mx:DateField id="startDate" width="100%" selectedDate="{_documentDetail.startDate}" formatString="{resourceManager.getString('eRequestAppli', 'dateformat')}" editable="false"/>
</mx:FormItem>
<mx:FormItem label="{resourceManager.getString('eRequestAppli','comments')}:" labelWidth="160" width="100%" height="79">
<mx:TextArea id="comments" width="100%" height="100%" text="{_documentDetail.comment}" editable="false"/>
</mx:FormItem>
</mx:VBox>
</mx:TitleWindow>
here's how i call it:
private function show(detail:Detail, readOnly:Boolean=false):void {
var popup:fxc_ProposalDetail =
fxc_ProposalDetail(PopUpManager.createPopUp(UIComponent(Application.application), fxc_ProposalDetail, true));
popup.documentDetail = detail;
popup.readOnly = readOnly;
}
Thanks for posting the code. Now I might be able to help.
Where are you handling the close event of the popup? Besure to use something like this:
private function handleCloseEvent():void {
PopUpManager.removePopUp(this);
}
Besides that it appears your problem has to do with the following:
Because a module is loaded into a child domain, it owns class definitions that are not in the main application’s domain. For example, the first module to load the PopUpManager class becomes the owner of the PopUpManager class for the entire application because it registers the manager with the SingletonManager. If another module later tries to use the PopUpManager, Adobe ® Flash® Player throws an exception.
The solution is to ensure that managers such as PopUpManager and any other shared services are defined by the main application (or loaded late into the shell’s application domain). When you promote one of those classes to the shell, the class can then be used by all modules. Typically, this is done by adding the following to a script block:
import mx.managers.PopUpManager;
private var popUpManager:PopUpManager;
The module that first uses the component owns that component’s class definition in its domain. As a result, if another module tries to use a component that has already been used by another module, its definition will not match the existing definition. To avoid a mismatch of component definitions, create an instance of the component in the main application. The result is that the definition of the component is owned by the main application and can be used by modules in any child domain.
see: http://help.adobe.com/en_US/flex/using/WS2db454920e96a9e51e63e3d11c0bf69084-799a.html
for a better understanding of modules.
as suggested before, reusing the popup instead of creating a new one each time seems to have solved the issue.

Flex CheckBox Question

<mx:DataGrid x="10" y="10" width="180" height="302" id="dgActions" dataProvider="{actionCollection}">
<mx:columns>
<mx:DataGridColumn headerText="Action" dataField="name"/>
<mx:DataGridColumn headerText="" dataField="setting" width="30" rendererIsEditor="true">
<mx:itemRenderer >
<mx:Component>
<mx:Box width="100%" height="100%" horizontalAlign="center" verticalAlign="middle">
<mx:CheckBox selected="{data.setting}" click="setActionSetting()">
<mx:Script>
<![CDATA[
private function setActionSetting(){
data.setting = String(this.selected);
}
]]>
</mx:Script>
</mx:CheckBox>
</mx:Box>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
</mx:columns>
</mx:DataGrid>
For some reason I'm getting an error at the data.setting= String(this.selected) line which says "Access to possibly indefined property selected through a reference with static type".
[edit] The solution to the above problem (albeit not the entire mess) was that once you're inside a <mx:Component> tag you are within the scope of said component. To get access to the script and nodes outside this component you have to use the outerDocument object.
[end edit]
I'm not sure what it's expecting, I'm assuming that it's going to pass the true/false value of the selected(ness) of the checkbox into the method, but it appears not to understand what "this" is, in this context.
Am I doing something obviously wrong? All I want is for the data source to reflect the change in the status that it initially fed into the checkbox.
EDIT:
I just noticed that when I add trace('foo') to the function, it never writes anything back to the console. Is the checkbox's native behavior (and event capture) preventing it from bubbling past to my function?
Additionally, when I add references to objects outside in the rest of the document, it tells me it doesn't recognize them. I'm totally confused as to how Flex scopes things... any additional guidance or links to reference would be really handy.
this in this (ha) case is referring to the component renderer and not the surrounding class (or the checkbox, datagridcolumn, datagrid, etc). You are really better off breaking the renderer out into a real component so you won't be obfuscating the scope as much as when the inline component approach is used.
Peter Ent's series on itemRenderers is extremely useful and should explain everything you want to know on the subject.
If I had to guess "this" is the mx:Script element, try "parent.selected".
CheckBox.selected requires a Boolean value. The fact that you're setting data.setting to a String value tells me that data.setting is NOT a Boolean.
So, after a great deal of agony I have finally figured out how this all works....
Joel is on the right track, this doesn't refer to what you would hope it would refer to (namely the checkbox). Additionally, even if you pass this into the method FROM the checkbox node, it refers to the parent wrapper class and not the checkbox itself. So, the solution is to pass in the event, and then access its target, which FINALLY is the checkbox. And then you're home.
In other words...
<mx:CheckBox selected="{data.setting}" click="setActionSetting(event)">
<mx:Script>
<![CDATA[
private function setActionSetting(e:Event):void{
data.setting = e.target.selected;
trace("n=" + data.name + " set to " + data.setting);
//the name is the other piece of the data that I omitted for clarity
}
]]>
</mx:Script>
</mx:CheckBox>

Can i create multiple itemrenderer for a datagrid column in flex?

I actually wanted to create an itemrenderer which will contain below code :
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="0" height="0" >
<mx:Label text="{data.xval}" />
<mx:Spacer width="100%" />
<mx:Image source="edit.gif" width="16" toolTip="Edit" />
</mx:HBox>
Now when i click on the image datgrid column should be editable, for that i am using a textinput as an itemeditor. Now i also want to highlight all those cell for this datagrid column which are having data more than 8 characters. I am able to do this separately using 2 different itemrenderers. but i want to get this all together. Can anyone pls help me to do this? Can we have multiple itemrenderer for any datagrid column?
Please let me know if my question is not clear to you?
Thanking you in advance.
One way to do it is to create a function that returns the name of the style that you want to use for highlighting, and call that function by databinding to the style property of your HBox. For example, if you had one css class named highlight and one named normal, you could use this function:
public function highlight(data:String):String
{
if(data.length >= 8)
{
return "highlight";
}
return "normal";
}
And call it like this:
<mx:HBox styleName="{highlight(data.xval)}">
...
</mx:HBox>

Flex:: How do I prevent in drag and drop, dragging onto self

I have a Flex question. I was wondering, if I have two list boxes, and I want to drag and drop between both of them, how do I prevent the user from dragging onto the same list (thus duplicating the item? I cannot have a situation where that is the case. Thanks guys
Haven't tested it but I guess something like this should work:
Listen to dragStart event on both lists and set a source variable depending on event.target. Now listen to the dragDrop event on both lists and call event.preventDefault() if the source is same as the target.
Here are some simple functions I made while building a working application I used to wrap my head around flex drag and drop. I was looking for a way to have multiple lists with drag and drop functionality that would not interfere with each other. Also, I didn't want to deal with copying list data around.
private function onlyAllowMoveDragOverHandler(event:DragEvent):void {
event.preventDefault();
event.currentTarget.showDropFeedback(event);
DragManager.showFeedback(DragManager.MOVE);
}
private function allowDropOnlyIfInitiatorEqualsComponent(event:DragEvent, component:IUIComponent):void {
event.preventDefault();
if (event.dragInitiator == component) {
DragManager.acceptDragDrop(event.target as IUIComponent);
}
else {
DragManager.showFeedback(DragManager.NONE);
}
}
And I in use in my mxml:
<mx:List
x="10"
y="170"
id="availableLangsList"
dataProvider="{availableLangs}"
width="100"
height="200"
dragEnabled="true"
dragMoveEnabled="true"
dropEnabled="true"
dragOver="onlyAllowMoveDragOverHandler(event);"
dragEnter="allowDropOnlyIfInitiatorEqualsComponent(event, selectedLangsList);"
dragComplete="selectedLangs.refresh();"
/>
<mx:Label x="129" y="153" text="list 4"/>
<mx:List
x="129"
y="170"
id="selectedLangsList"
dataProvider="{selectedLangs}"
width="100"
height="200"
dragEnabled="true"
dragMoveEnabled="true"
dropEnabled="true"
dragOver="onlyAllowMoveDragOverHandler(event);"
dragEnter="allowDropOnlyIfInitiatorEqualsComponent(event, availableLangsList);"
dragComplete="availableLangs.refresh();"
/>
I found the solution, which im not sure would work for anyone else. I basically had in my two lists:
`
<mx:List id="srcList" dataProvider="{_source}"
allowMultipleSelection="true"
enabled="{enabled}"
labelField="{labelField}"
iconFunction="iconFunction"
dragEnabled="true"
dropEnabled="true"
dragDrop="doDragDrop(event);"
width="100%"
height="100%"
/>
</mx:VBox>
<mx:VBox paddingTop="50">
<mx:Button label="->" enabled="{enabled}" click="add()"/>
<mx:Button label="<-" enabled="{enabled}" click="rem()"/>
</mx:VBox>
<mx:VBox width="100%" height="100%">
<mx:Label text="{right_col_heading}" />
<mx:List id="dstList" dataProvider="{_destination}"
allowMultipleSelection="true"
enabled="{enabled}"
dragEnabled="true"
dropEnabled="true"
dragDrop="doDragDrop(event);"
width="100%"
height="100%"
labelField="{labelField}"
iconFunction="iconFunction"
verticalAlign="center"
/>`
I basically added a dragMoveEnabled = "true" to both lists and now basically not re-add to the same list an item of itself, but just move the order (which doesnt matter to me as its a soap send and the back-logic would put it in the correct order anyway).
In my case, I used a HashCollection (which extends ArrayCollection) [just google it, you'll find the component]. The dataprovider is bind to this has collection. You would add items to the collection with: dataprovider.put (key, object) instead of dataprovider.addItem(object).
The "hash" will ensure uniqueness in the collection. SO, even if the user drag-drop something that already exists in the hash, the original value would get replaced with the new object (but it wouldn't matter because it's the same value).
The "key", however, must be unique.... otherwise, the hash idea won't work.
Thanks Brice, Those functions were helpful.
For them to work in Spark Lists just update the first function as follows with createDropIndicator instead of showDropFeedback and stop passing the event.
private function onlyAllowMoveDragOverHandlerS(event:DragEvent):void {
event.preventDefault();
event.currentTarget.createDropIndicator();
DragManager.showFeedback(DragManager.MOVE);
}

Resources