How to reload checkbox in itemrenderer - apache-flex

I am having a problem with a checkbox in my datagrid. It pulls in a dataprovider (xml file) and I am using it to set the checkbox selection with a custom itemrenderer. I then save the datagrid, when updated, back to the xml file.
The checkbox, when clicked, saves to the xml fine.. I know this because when I reload the application it shows the correct result. However, when I just refresh the dataprovider without closing out the flex application then the checkboxes revert back to what they were before the change.
So here is the custom checkbox:
<?xml version="1.0" encoding="utf-8"?>
<mx:CheckBox xmlns:mx="http://www.adobe.com/2006/mxml" >
<mx:Script>
<![CDATA[
override public function set data( value:Object ):void{
super.data = value;
trace("data = " + data.#hidden);
if(data.#hidden == "true")
{
this.selected = true;
}else
{
this.selected = false;
}
}
]]>
</mx:Script>
</mx:CheckBox>
And here is where I am calling it:
<mx:DataGridColumn width="75" headerText="hide?" dataField="#hidden">
<mx:itemRenderer>
<mx:Component>
<local:itemRendCheckBox />
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
I imagine what is happening is that everything is updating except the custome itemrenderer. Is there something I can do to tell the checkbox to refresh with the dataprovider?

I think I figured it out, I was calling the datagrid to refresh, which is different then refreshing the actual dataprovider. So instead of myDataGrid.send() I called dataProv.dataprovider.refresh();

Related

Weird Behaviour-CheckBoxes as ItemRenderer within Flex DataGrid - FLEX 3

I'm having this weird behaviour in a datagridColumn which I've customized to have its cells rendered as checkBoxes rather than the dafault itemRenderer (i.e. strings). The relevant code is as follows:
<mx:DataGridColumn sortable="false" textAlign="center" headerText="" width="20" dataField="colCB">
<mx:itemRenderer>
<mx:Component>
<mx:CheckBox selected="true">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
public function change():void{
//TODO
}
]]>
</mx:Script>
</mx:CheckBox>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
Well, what happens is that whenever I check a checkbox, other checkboxes (in other rows) get randomly checked or unchecked, and if I scroll down or up they once again randomly get selected or unselected.
Can anybody help me with this one?
Thanks is advance
PS by the way, I've suppressed the starting "<" in the tags because it was messing with the textEditor, but in my code they're there
My guess is that the issue isn't that the checkboxes are getting randomly checked and unchecked. The DataGrid recycles its itemRenderers for better memory performance. What's likely happening is that you're checking a CheckBox on an itemRenderer and start scrolling, that itemRenderer with the checked box is getting reused to display other records with the selected="true" value still set.
What I would do is create an itemRenderer component and override the set data method to set the checkbox's selected value to what it should be.
Some sample code off the top of my head for the itemRenderer (you'll want to adjust it for your use):
<mx:HBox horizontalScrollPolicy="off" verticalScrollPolicy="off">
<fx:Script>
<![CDATA[
override public function set data(value:Object):void
{
super.data = value;
if (value["myCheckBoxData"] != null)
{
myCheckBox.selected = Boolean(value["myCheckBoxData"]);
}
validateDisplayList();
}
]]>
</fx:Script>
<mx:CheckBox id="myCheckBox" />
</mx:HBox>

Accessing DataGridColumn item renderer variable

Within a DataGrid, I have a DataGridColumn that uses a custom component as the item renderer. Within the component, I have an ArrayCollection that stores a set of value objects. My problem is that I cannot access the ArrayCollection values from outside of the item renderer component. Does anyone know how it would be possible to do this? I have posted a code snippet below.
<mx:Script>
<![CDATA[
// Cannot access arrFiles from here.
]]>
</mx:Script>
<mx:DataGrid editable="true">
<mx:columns>
<mx:DataGridColumn id="dgcUpload" width="130" headerText="Uploaded Files"
editable="false">
<mx:itemRenderer>
<mx:Component>
<mx:VBox>
<mx:Script>
<![CDATA[
[Bindable]public var arrFiles:ArrayCollection = new ArrayCollection();
]]>
</mx:Script>
</mx:VBox>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
</mx:columns>
</mx:DataGrid>
Is this possible?
Thank you in advance for any assistance,
Orville
I would create a custom MXML Box component as the rendered with a label (myLabel) as a child. Set the data provider for the DataGrid to the array. In the custom MXML component override the set data method which is called each time the data is rendered for each row and set the label to the current value passed in:
override public function set data(value:Object):void{
myLabel.text = value.myTextForLabel;
}
If the field in the ArrayCollection (myArrayCollection) is always the same for the label, then just set the DataGrid data provider to the ArrayCollection and the dataField property of the column to the appropriate value (myText):
<mx:DataGrid editable="true" dataProvider="myArrayCollection">
<mx:columns>
<mx:DataGridColumn id="dgcUpload" width="130" dataField="myText" headerText="Uploaded Files"
editable="false">
</mx:columns>
</mx:DataGrid>
It is possible depending on how you want to access it. You can access the property of a specific item being rendered by the itemRenderer by calling the itemToItemRenderer function on the datagrid. That gives you an instance of that specific itemRenderer and you can call the arrFiles variable for that item.
Here's an example
protected function datagrid1_clickHandler(event:MouseEvent):void
{
var obj:Object = dgcUpload.itemToItemRenderer(dgcUpload.selectedItem);
var newArray:ArrayCollection = obj.arrFiles;
}
I call that when something is clicked on the DataGrid and I want to access the arrFiles variable for the selected item.
Is that what you're looking for?
=Ryan

Is it possible to store an array in a DataGridColumn in Flex?

I have a datagrid column with a button that opens a modal dialog box allowing the user to upload multiple files. In the code below, the browseAndUpload() method does that. When the user finished uploading files and closes the upload box the closeUpload() method is called. I know for a fact that the uploaded files are being copied into arrFiles.
The problem I am having is that the repeater will not show the files in arrFiles. Here is the code:
<mx:DataGridColumn id="dgcUpload" width="42" headerText="Uploaded Files"
editable="false">
<mx:itemRenderer>
<mx:Component>
<mx:VBox>
<mx:Script>
<![CDATA[
[Bindable]public var arrFiles:ArrayCollection = new ArrayCollection();
public var fileUpload:FileUpload = new FileUpload();
private function browseAndUpload(event:MouseEvent):void
{
fileUpload = FileUpload(PopUpManager.createPopUp(this, FileUpload, true));
fileUpload.addEventListener(CloseEvent.CLOSE, closeUpload);
fileUpload["btnClose"].addEventListener("click", closeUpload);
}
private function closeUpload(event:Event):void
{
arrFiles = fileUpload.arrFiles;
}
]]>
</mx:Script>
<mx:HBox paddingLeft="3" paddingRight="3">
<mx:Button width="36" label="..." click="browseAndUpload(event)"/>
</mx:HBox>
<mx:Repeater id="rpFiles" dataProvider="{arrFiles}">
<mx:Label text="{FileVO(rpFiles.currentItem).name}"/>
</mx:Repeater>
</mx:VBox>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
Thank you in advance for any insight,
Orville
Got it! I made the following changes:
private function closeUpload(event:Event):void
{
arrFiles = fileUpload.arrFiles;
rpFiles.dataProvider = arrFiles;
}
<mx:Repeater id="rpFiles">
<mx:Label text="{FileVO(rpFiles.currentItem).name}"/>
</mx:Repeater>
You are assigning fileUpload.arrFiles directly to arrFiles. Is the former an Array or ArrayCollection? You might need to do arrFiles = new ArrayCollection(fileUpload.arrFiles);
That being said, I hate flex binding and generally avoid it because it can be unreliable. In your case, I'd write my own AS3 component that implements the ItemRenderer and then assign the repeater's dataprovider manually when it changes. You will have more control over the behavior if you do it that way. And a much easier time debugging.

Disable TextArea ala Facebook when clicked outside TextArea

I have a textArea and a Button - I want the Button to disappear when the user clicks anywhere in the app window EXCEPT the "Send" button
<mx:Button x="306" y="168" label="Button" id="btn" click="Alert.show('Button clicked')"/>
<mx:TextArea x="138" y="146" focusOut="btn.visible=false" focusIn="btn.visible=true"/>
I tried calling btn.visible=false when TextArea loses focus (using focusOut event) - if I click anywhere in the app it works, but it also works when I click the Button - the TextArea focusOut event is processed first and the click for the button later - can someone please help out with this?
Thanks!
were u trying to override the default focusOut handler? That doesn't work...
private function setBtnNotVisible():void
{
btn.visible=false;
}
override protected function focusOutHandler(event:FocusEvent):void
{
callLater(setBtnNotVisible);
}
Neither does writing my own event handler...
private function setBtnNotVisible():void
{
btn.visible=false;
}
private function focusOutHandler2(event:FocusEvent):void
{
callLater(setBtnNotVisible);
}
--------------------- Full Code ------------------
##### Try clicking INSIDE the textArea and then on the button, I cannot catch the Alert
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
private function onFocusOut(event:FocusEvent):void{
callLater(
function ():void{
btn.visible=false;
}
)
}
]]>
</mx:Script>
<mx:Button x="306" y="168" label="Button" id="btn" click="Alert.show('Button clicked')"/>
<mx:TextArea x="138" y="146" focusOut="onFocusOut(event)" focusIn="btn.visible=true"/>
</mx:Application>
I previously said use call later, but on testing it doesn't work, sorry for wasting anyone's time. Instead you need to use the focus manager something like this: I tested this and it seems solid.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.managers.FocusManager; //pull in the manager
private function onFocusOut(event:FocusEvent):void{
if(getFocus() != null){ //in case focus goes outside the flash player
if(getFocus().name == "btn"){ //the focus went to the item with the ID "btn"
return; //do nothing, let the click handler work
}else{ //any other item gets focus
btn.visible=false; //disappear
}
}
}
private function clickHandler():void{ // made it it's own function so do more than just alert
Alert.show('Button clicked');
btn.visible=false;
}
]]>
</mx:Script>
<mx:Button x="306" y="168" label="Button" id="btn" click="clickHandler();"/>
<mx:TextArea x="138" y="146" focusOut="onFocusOut(event)" focusIn="btn.visible=true"/>
</mx:Application>

Flex - Avoid click event on container when enclosed component is clicked

I have a Flex application where I'm using a Canvas to contain several other components. On that Canvas there is a Button which is used to invoke a particular flow through the system. Clicking anywhere else on the Canvas should cause cause a details pane to appear showing more information about the record represented by this control.
The problem I'm having is that because the button sits inside the Canvas any time the user clicks the Button the click event is fired on both the Button and the Canvas. Is there any way to avoid having the click event fired on the Canvas object if the user clicks on an area covered up by another component?
I've created a simple little test application to demonstrate the problem:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
private function onCanvasClick(event:Event):void {
text.text = text.text + "\n" + "Canvas Clicked";
}
private function onButtonClick(event:Event):void {
text.text = text.text + "\n" + "Button Clicked";
}
]]>
</mx:Script>
<mx:Canvas x="97" y="91" width="200" height="200" backgroundColor="red" click="onCanvasClick(event)">
<mx:Button x="67" y="88" label="Button" click="onButtonClick(event)"/>
</mx:Canvas>
<mx:Text id="text" x="97" y="330" text="Text" width="200" height="129"/>
</mx:Application>
As it stands when you click the button you will see two entries made in the text box, "Button Clicked" followed by "Canvas Clicked" even though the mouse was clicked only once.
I'd like to find a way that I could avoid having the second entry made such that when I click the Button only the "Button Clicked" entry is made, but if I were to click anywhere else in the Canvas the "Canvas Clicked" entry would still appear.
The event continues on because event.bubbles is set to true. This means everything in the display heirarchy gets the event. To stop the event from continuing, you call
event.stopImmediatePropagation()
Laplie's answer worked like a charm. For those interested the updated code looks like this:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
private function onCanvasClick(event:Event):void {
text.text = text.text + "\n" + "Canvas Clicked";
}
private function onButtonClick(event:Event):void {
text.text = text.text + "\n" + "Button Clicked";
event.stopImmediatePropagation();
}
]]>
</mx:Script>
<mx:Canvas x="97" y="91" width="200" height="200" backgroundColor="red" click="onCanvasClick(event)">
<mx:Button x="67" y="88" label="Button" click="onButtonClick(event)"/>
</mx:Canvas>
<mx:Text id="text" x="97" y="330" text="Text" width="200" height="129"/>
</mx:Application>
The only difference is the one additional line in the onButtonClick method.
I have 2 ideas, first try this:
btn.addEventListener(MouseEvent.Click,function(event:MouseEvent):void{
event.stopImmediatePropagation();
...
});
if that doesn't work, see if you can add the click listener to the canvas and not the button and check the target property on the event object. something like:
btn.addEventListener(MouseEvent.Click,function(event:MouseEvent):void{
if(event.target == btn){
...
}
else{
...
}
});
Again, these are some ideas of the top of my head...I'll create a small test app and see if these work...

Resources