Flex Help: Repeaters not repeating in an Accordian control - apache-flex

I am having a issue with databinding child repeaters inside an accordion control, hopefully you can help...
I have an accordion in a ViewStack (of which,that ViewStack is also in another top-level ViewStack). I have a repeater in each child of the accordion control. The component looks like such:
<mx:Box
xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="init()"
>
<mx:ViewStack&gt
...
<mx:Accordion creationComplete="accordianInit()">
<mx:Box label="Groups" width="100%">
<mx:Repeater id="rpGroups" width="100%">
<mx:CheckBox id="chkGroups"
label="{rpGroups.currentItem.name}" />
</mx:Repeater>
</mx:Box>
<mx:Box label="Contacts">
<mx:Repeater id="rpContacts">
<mx:CheckBox id="chkContacts"
label=quot;{rpContacts.currentItem.full_name}" />
</mx:Repeater>
</mx:Box>
</mx:Accordion>
...
</mx:ViewStack&gt
<mx:Box>
The problem is that if I bind the 2 repeaters in the init function, then both repeaters don't show any data. If I bind the repeaters in the accordianInit function, then only the first repeater (rpGroups) gets databound...
Where should I be data binding the repeaters so that both repeaters repeat properly?
Hopefully this makes sense, if not I can elaborate more, any help is appreciated.

Bind the dataProvider of the repeater to the source in MXML itself:
<mx:Repeater dataProvider="{the_data}" ... />
The reason you're seeing the behavior you are is because the Accordion (and ViewStack) does not create all of it's children right away. It only creates the child that is visible (so, the first Box, and the first ViewStack child initially).
Because of this behavior, when you try to assign data to the repeaters in the first init() event handler the repeaters have no instantiated container to repeat children into. When you assign data to the repeaters in accordionInit() then only the first Box has been created, which is why only the first repeater works.
If you don't want to bind to the data via the dataProvider attribute of the Repeater tag (as I've shown above), then you can use a change handler on the Accordion to set the repeater data as the user changes panes (because as the user clicks into the panes, they get created by the Flex framework).
All of this comes about from the creationPolicy property: http://livedocs.adobe.com/flex/3/html/layoutperformance_05.html

Related

'setProgress not a function' error while setting progress value of a progress bar

I want to set value of a progress bar in an accordian but I am encountering 'setProgress is not a function' error. Any idea what's wrong with following code.
Observation:
If I move the progress bar out of the Accordian then the error goes away and the progress bar appears fine.
I want to set the progress bar eventually to {repMonitor.currentItem.threatLevel} but for now I am just testing with hypothetical threat value i.e 60
<mx:Accordion id="monAccordian" includeIn="Monitoring" x="10" y="10" width="554" height="242" change="monAccordianChange()" >
<mx:Repeater id="repMonitor" dataProvider="{monitoringArray}">
<mx:Canvas width="100%" height="100%" label="{repMonitor.currentItem.firstName+' '+ repMonitor.currentItem.lastName}" >
<mx:Image x="10" y="10" source="{repMonitor.currentItem.imageName}" width="175" height="118"/>
<s:Label x="200" y="14" text="Threat Level:"/>
<mx:ProgressBar x="200" y="30" mode="manual" label="" id="bar" width="200" creationComplete="bar.setProgress(60,100);" />
</mx:Canvas>
</mx:Repeater>
</mx:Accordion>
This stems from the fact that your ProgressBar is in a repeater. You cannot reference the repeated items by id because you would have a variable number of ProgressBars with id "bar".
There are also special considerations when using event listeners inside of Repeater objects:
Event handlers in Repeater components
When a Repeater component is busy
repeating, each repeated object that
it creates can bind at that moment to
the Repeater component's currentItem
property, which is changing as the
Repeater component repeats. You cannot
give each instance its own event
handler by writing something like
click="doSomething({r.currentItem})"
because binding expressions are not
allowed in event handlers, and all
instances of the repeated component
must share the same event handler.
Repeated components and repeated
Repeater components have a
getRepeaterItem() method that returns
the item in the dataProvider property
that was used to produce the object.
When the Repeater component finishes
repeating, you can use the
getRepeaterItem() method to determine
what the event handler should do based
on the currentItem property. To do so,
you pass the
event.currentTarget.getRepeaterItem()
method to the event handler. The
getRepeaterItem() method takes an
optional index that specifies which
Repeater components you want when
nested Repeater components are
present; the 0 index is the outermost
Repeater component. If you do not
specify the index argument, the
innermost Repeater component is
implied.
You can read more about this in the Repeater docs.

Flex ViewStack With A Repeater - Repeater Gets Placed At The Bottom

I have a problem when creating a ViewStack, with a repeater along with other components (i.e. vBox) inside the ViewStack. The repeaters get placed at the bottom of the view. Despite the repeater is listed before other components, it is placed at the bottom.
Is there a workaround where I can get the repeater drawn before the other components?
I googled this and someone else asked the same question on another site, but no one answered. I am pasting sample code from their post. The repeater is drawn last.
Thanks
Here is an example:
<mx:ViewStack id="viewStack" width="100%" height="100%" backgroundColor="#FFFFFF"
backgroundAlpha="1" paddingLeft="5" paddingRight="5" paddingBottom="5">
<mx:Repeater id="editorsRP" dataProvider="{dynamicFields}"
repeatEnd="organizeViewStack();" width="100%" height="100%">
<editor:DynamicFieldEditor label="{FieldGroup(editorsRP.currentItem).name}"
width="100%" height="100%" fields="{FieldGroup(editorsRP.currentItem).fields}" dataProvider="{details}" />
</mx:Repeater>
<editor:NotesEditor id="notesEditor" width="100%" height="100%" label="Notes"
enabled="false" notesProvider="{attachment}" />
</mx:ViewStack>
I believe the problem is when the dataProvider is having it value set / changed.
This isn't shown in the code above, so it's hard to know. However, whenever the value of this is changed, the existing children created by the repeater will be removed from the parent, and new ones will be added.
If the ViewStack's children have already been created, then the children from the repeater will be placed after the existing children.
For example:
<mx:TabNavigator width="500">
<mx:VBox label="Static 1" />
<mx:Repeater dataProvider="{['a','b','c']}">
<mx:VBox label="From repeater" />
</mx:Repeater>
<mx:VBox label="Static 2" />
</mx:TabNavigator>
In this instance, the dataProvider is set during the initialization of the repeater, so the tabs appear in order:
¦ Static 1 ¦ From Repeater ¦ From Repeater ¦ From Repeater ¦ Static 2 ¦
However, if the value of the dataProvider changes (or the dataProvider dispatches a CollectionChangeEvent), then the 3 repeater children would be removed, and new children added. This would leave the tab order:
¦ Static 1 ¦ Static 2 | From Repeater ¦ From Repeater ¦ From Repeater ¦
I notice you've wired an organizeViewStack method to the repeatEnd event handler - although the code for the method isn't shown.
This looks like the best way of managing this - reshuffling the order of the children once they've been set.
I'm guessing a bit, but
All of a ViewStack's children must be a container. The Repeater is not a container. So, it's placement as a ViewStack children is irrelevant, because for all intents and purposes it is ignored.
However, when the repeater executes it creates a bunch of containers and adds them to the parent (AKA ViewStack); thus giving them a different Z-order than what you might expect from the code.
Are you trying to create many different children of the ViewStack? Or are you expecting the "DynamicFieldEditors" to be stacked somehow?

How to use out-of-datagrid scope variable inside an ItemRenderer?

I'm binding an array of items to a data grid using ItemRenderer. I use the data variable to control the bindable data. I also have someComponentVariable that need be inserted into every row but its declared at the component scope, so the data grid doesn't seem to reconize it (compile error).
How can I use this variable (someComponentVariable) inside the ItemRenderer?
Code Example
<mx:DataGrid id="userBonusesGrid" width="100" height="248" showHeaders="false" wordWrap="true">
<mx:columns>
<mx:DataGridColumn headerText="" width="36">
<mx:itemRenderer>
<mx:Component>
<mx:VBox verticalAlign="middle" horizontalAlign="center">
<ns1:SidePanelBonus
bonusName="{data.name}" description="{data.description}"
arrow="{someComponentVariable}">
</ns1:SidePanelBonus>
</mx:VBox>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
</mx:columns>
</mx:DataGrid>
If someComponentVariable is a public property of the class enclosing DataGrid, you can use outerDocument to access it from a component.
<ns1:SidePanelBonus bonusName="{data.name}" description="{data.description}"
arrow="{outerDocument.someComponentVariable}">
</ns1:SidePanelBonus>
See the "using the Component tag" section in Creating inline item renderers and editors for more info about outerDocument
No you can not use it at all. Each itemRenderer in data grid can only access the item for which the renderer was created. And this is done purposely because itemRendrers change dynamically, they are not bound for data forever, when you scroll, the items get scrolled not the renderers, they remain in same position or they might change, but corresponding item renderer's data always changes when you scroll. They dont share one to one relationship.
The only solution is to pass the data in the item in the form of some parent child relationship.

Flex: Accessing functions / components accross mxml pages

For simplicity lets say I have two flex mxml pages.
form.mxml
button.mxml
If the form.mxml page had the following code, it should work fine:
<custom:SelectView dSource="{_thedata}" id="form" visible="false">
</custom:SelectView>
<mx:LinkButton label="Show" id="lbShow" click="form.visible=true;>
<mx:LinkButton label="Show" id="lbHide" click="form.visible=false;>
But if the code was like:
form.mxml
<custom:SelectView dSource="{_thedata}" id="form" visible="false">
</custom:SelectView>
button.mxml
<mx:LinkButton label="Show" id="lbShow" click="form.visible=true;>
<mx:LinkButton label="Show" id="lbHide" click="form.visible=false;>
how can I make a call from button.mxml to change form.mxml
---- a bit more details ---
My page actually looks like this: where query:AdvancedSearchFields is basically including a flex form into the page, and I want it to show/hide the custom view below after the search is complete.
<query:AdvancedSearchFields searchType="projects" searchCategory="advanced" visible="true" id="AdvancedSearch" />
<custom:SelectView dSource="{_searchResults}" id="sv" visible="false">
You could write a custom method that handles the button click events and raises a custom event. Then in form.mxml you can handle that event.
Splitting it up like this is a bit cleaner, as it makes the button.mxml file work on its own. Having Button.mxml have a direct reference to your form causes a tight-coupling between the two, and generally you should avoid tight-coupling.
EDIT: I just had another thought that also avoids tight-coupling and is a bit simpler:
form.mxml
<custom:SelectView dSource="{_thedata}" id="form" visible="{buttons.showForm}">
</custom:SelectView>
<!-- include your buttons.mxml component using an ID of "buttons" -->
buttons.mxml
<mx:Script>
<![CDATA[
[Bindable] public var showForm:Boolean = true;
]]>
</mx:Script>
<mx:LinkButton label="Show" id="lbShow" click="this.showForm=true;">
<mx:LinkButton label="Hide" id="lbHide" click="this.showForm=false;">
This essentially emulates using a custom event by using variable binding. Any time the showForm variable in buttons changes the visible property of the SelectView will be updated via the bindings. This is lighter-weight than creating a custom event (though I think custom events are a bit better of a design for it).
Your button.mxml class must have a reference to the instance of the 'form' class which will be affected. Then it can operate on it directly:
Button.mxml:
<mx:Script>
<![CDATA[
[Bindable] public var myForm:MyFormClass;
]]>
</mx:Script>
<mx:LinkButton label="Show" id="lbShow" click="myForm.form.visible=true;">
<mx:LinkButton label="Show" id="lbHide" click="myForm.form.visible=false;">
Generally, the most logical place to set this variable is in the parent of your Button class.
If you need to deal with this problem more often, I'd suggest using an MVC framework like PureMVC. It's set up so that you have a Mediator object that listens for events from MXML components, then sends a notification which can be picked up by any other mediator. Then that mediator can manipulate its own visual component based on the notification and its associated data.
In the context of what you're doing (the simple version), you're okay with the basic solution. But once you're dealing with four or five or more components with lots of logic, you will not be happy at all.

Dynamic RadioButtons

Our resident Flex expert is out for the day and I thought this would be a good chance to test this site out. I have a dropdown with a dataProvider that is working fine:
<ep:ComboBox id="dead_reason" selectedValue="{_data.dead_reason}"
dataProvider="{_data.staticData.dead_reason}"
labelField="#label" width="300"/>
The ComboBox is custom but I'm not sure if that matters for the question. I need to change the combo box to radios (all in one group) but maintain the dynamic options. In other words, what is the best way to generate dynamic RadioButtons?
Try using an <mx:Repeater> Something like:
<mx:Repeater dataProvider="{_data.staticData.dead_reason}">
<mx:RadioButton groupName="reasons" ...>
</mx:Repeater>
<mx:RadioButtonGroup id="RDO_Group"/>
<mx:Repeater id="myRepeater" dataProvider="{_data.staticData.dead_reason}">
<mx:RadioButton id="rdo" label="{myRepeater.currentItem}" value="{myRepeater.currentItem}" groupName="RDO_Group"/>
</mx:Repeater>
is the best way.

Resources