using an image item render in a flex data grid - apache-flex

I'm attempting to add an image to a datagrid item render dynamically in flex.
Here is my DataGrid code
The value of "str" in the getImagePath function is correct.
<?xml version="1.0" encoding="utf-8"?>
<mx:DataGrid xmlns:mx="http://www.adobe.com/2006/mxml"
doubleClickEnabled="true">
<mx:Script>
<![CDATA[
private function userLabelFunction(item:Object, column:DataGridColumn):String
{
return item.user.username;
}
private function getImagePath(item:Object, column:DataGridColumn):String
{
var str:String=item.track["artwork-url"]
if (str == "")
{
str=item.user["avatar-url"];
}
return str;
}
]]>
</mx:Script>
<mx:columns>
<mx:DataGridColumn dataField="artwork-url"
headerText="Art"
itemRenderer="components.content.contents.datagrids.ImageRenderer"
labelFunction="getImagePath"/>
<mx:DataGridColumn dataField="title"
headerText="Title"
minWidth="100"/>
<mx:DataGridColumn dataField="user"
headerText="User"
labelFunction="userLabelFunction"/>
<mx:DataGridColumn dataField="bpm"
headerText="BPM"/>
</mx:columns>
</mx:DataGrid>
I cant manage to get that image url value into my item renderer
I've tried overriding the set data property like so
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml"
height="60"
verticalAlign="top"
creationComplete="init()">
<mx:Script>
<![CDATA[
import components.content.contents.containers.ContentContainerSoundCloud;
import mx.core.Application;
import mx.controls.dataGridClasses.DataGridColumn;
import com.adobe.viewsource.ViewSource;
[Bindable]
public var imgPath:String;
private function init():void
{
}
override public function set data(value:Object):void
{
super.data = value.track["artwork-url"];
imgPath =super.data.valueOf()
trace(imgPath)
}
]]>
</mx:Script>
<mx:Image source="{imgPath}" id="rowimage"/>
</mx:HBox>
But in doing so the trace output looks like so
<artwork-url>
<mx_internal_uid>7C98E149-1984-584C-7600-AD8940BF2A9C</mx_internal_uid>
</artwork-url>
I was expecting the "value" property in the set data to recieve the string I sent it from the get imagePathFunction but it fact it returns my entire XMLList.
What am I doing wrong?

Based on your trace output it looks like you are retrieving the wrong value from your dataProvider to set as the source. Without seeing your actual data, it's hard to know what the exact issue may be.
That said, I would re-work your itemRenderer a bit. First, you don't need to put a single image in an HBox. Just use an Image. Also, you should not need to specify the height in the Renderer, the DataGrid should take care of such positioning.
I also removed the creationComplete listener, since no code was in it. And instead of using binding, I just set the soruce property on the component in the data set method. I also set the super.data to the value, not a processed value; and I performed the processing when setting the value's source. The updated code is like this:
<?xml version="1.0" encoding="utf-8"?>
<mx:Image xmlns:mx="http://www.adobe.com/2006/mxml"
>
<mx:Script>
<![CDATA[
import components.content.contents.containers.ContentContainerSoundCloud;
import mx.core.Application;
import mx.controls.dataGridClasses.DataGridColumn;
import com.adobe.viewsource.ViewSource;
[Bindable]
public var imgPath:String;
override public function set data(value:Object):void
{
super.data = value;
this.source =value.track["artwork-url"];
trace(imgPath)
}
]]>
</mx:Script>
</mx:Image>
My preferred method in itemRenderers is to listen to the dataChange event, however that's just personal preference. There is nothing wrong w/ overriding the data set method.

Related

Pass value from Textinput to Controller file

I am currently trying to put together a simple Illustrator plugin, and coming from a design background this is proving to be quite a task, I have experience with JS, but not with Flex.
What I want to do is to have a panel in Illustrator, with an input field and a button. You type something in the input and press the button and a text frame with the desired text is added to the canvas.
But how do I pass the value from a mx:Textinput to the Controller.as file? I couldn't find an answer on the web.
This is my main.mxml file:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" historyManagementEnabled="false">
<mx:Script>
<![CDATA[
private var c:Controller = new Controller();
]]>
</mx:Script>
<mx:VBox height="100%" width="100%" verticalAlign="middle" horizontalAlign="center">
<mx:Label text="myVariable"></mx:Label>
<mx:TextInput name="TextValue"/> // I want the text value to be passed to the Controller class so I can pass it on to my JSX function
<mx:Button label="Run" click="c.run()"/>
</mx:VBox>
</mx:Application>
And this is my Controller.as file:
package
{
import flash.external.HostObject;
public class Controller
{
[ Embed (source="myScript.jsx" , mimeType="application/octet-stream" )]
private static var myScriptClass:Class;
public function run():void {
var jsxInterface:HostObject = HostObject.getRoot(HostObject.extensions[0]);
jsxInterface.eval( new myScriptClass ().toString());
//calling from AS to JSX
jsxInterface.myJSXFunction (myVariable); //This is where I want the value to be passed to
}
}
}
You might also pass the string directly to the c.run() call.
public function run(myString:String):void {
...
jsxInterface.myJSXFunction (myString)
...
and then
<mx:TextInput id="TextValue"/>
<mx:Button label="Run" click="c.run(TextValue.text)"/>
Just another approach.
Loic
First declare public property public var myTextValue : String; in your Controller.
Then declare bidirectional binding in your MXML <mx:TextInput text="#{c.myTextValue}"/>
Now you have myTextValue property always containing the actual value.
But bidirectional binding was introduced not that long time ago.
Alternatively, you can add a change event listener to your TextInput instance <mx:TextInput id="myTextInput" change="c.myTextValue = myTextInput.text"/>

Flex: DataGrid column formatting of numbers

I'm trying to format some numbers in a column of a DataGrid. I'm getting an error in my simplified test program below when I run it. All the examples I've seen so far have column data that are strings. Is there a way to do it using numbers? How to modify the code below to format the checking values?
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
[Bindable]
public var checking:Array = new Array(1000000.2222, 0, 1000);
private function myLabelFunction(item:Array, column:DataGridColumn):String {
var result:String;
result = myFormatter.format(item);
return result;
}
]]>
</fx:Script>
<fx:Declarations>
<s:NumberFormatter id="myFormatter"
fractionalDigits="2"
decimalSeparator="."
groupingSeparator=","
useGrouping="true"
negativeNumberFormat="0"
/>
</fx:Declarations>
<mx:DataGrid id="dg1" dataProvider="{checking}" >
<mx:columns>
<mx:DataGridColumn dataField="checking" headerText="Checking"
labelFunction="myLabelFunction" />
</mx:columns>
</mx:DataGrid>
</mx:Application>
Change filter function signature (item should be Object)
private function myLabelFunction(item:Object, column:DataGridColumn):String
Remove dataField="checking" from column.
While the label function will certainly work -- I usually prefer an ItemRenderer for things like this. You override the render function and then you can display whatever it is in the grid view "box" however you like.
A decent example is here. Scroll down about 1/4 the way down for a DataGrid example.
In case of object you must use/No caso de objeto, deve-se usar:
private function myLabelFunction(item:Object, column:GridColumn):String {
var result:String;
result = myFormatter.format(item[column.dataField]);
return result;
}

Calling Application.application.enable from a TitleWindow in a different mxml component

I have a Flex RIA App, and in the application tag there is a button when it's pressed calls upon a TitleWindow from another .mxml file, and sets
application.enable = false
That way the user can't use any of the components in the application, and still can use the components in the TitleWindow.
The problem is when the TitleWindow is closed I want it to restore the application back to
application.enable = true
Which enables the application once again. But I can't call that code from inside the TitleWindow .mxml
How can I do it?
Here is the Source:
Loja.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="585" height="450" xmlns:ns1="com.*">
<mx:Style source="theme/simplicitygray.css" />
<mx:Script>
<![CDATA[
import mx.managers.PopUpManager;
private var clientid = 0;
public function openWindow() : void
{
if (clientid == 0)
{
PopUpManager.createPopUp(this,Login,false);
application.enabled = false;
} else {
PopUpManager.createPopUp(this,Conta,false);
application.enabled = false;
}
}
]]>
</mx:Script>
<mx:Panel x="10" y="40" width="565" height="400" layout="absolute">
</mx:Panel>
<mx:MenuBar x="10" y="10" width="565" height="22"></mx:MenuBar>
<mx:Button x="508" y="10" label="Aceder" click="openWindow();"/>
</mx:Application>
And one of the title windows. Once they are the same.
Login.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="350" height="200" creationComplete="centerWindow()" showCloseButton="true" close="closeWindow()" title="Login">
<mx:Script>
<![CDATA[
import mx.managers.PopUpManager;
public function centerWindow():void
{
PopUpManager.centerPopUp(this);
}
public function closeWindow():void
{
PopUpManager.removePopUp(this);
}
]]>
</mx:Script>
</mx:TitleWindow>
application is a static property of the Application class and can be called from the TitleWindow
public function closeWindow():void
{
PopUpManager.removePopUp(this);
Application.application.enabled = true;
}
BTW, There is another easier way to achieve the following:
That way the user cant use any of the components in the application, and still can use the components in the TitleWindow.
That is to use a modal popup. Set the third parameter of the createPopUp to true and that's it - you don't have to enable/disable the application manually: flex will take care of it.
PopUpManager.createPopUp(this,Login, true);
application will automatically become functional once you call removePopUp.
You can use custom events to enable this functionality, as described here.
Essentially, you set up a custom event in the class you are calling, then create a function that runs when the event is consumed. That way your 'Loja' will know when the 'Login' is done.

Flex DataGrid with ComboBox itemRenderer

I'm going spare trying to figure out the "correct" way to embed a ComboBox inside a Flex (3.4) DataGrid. By Rights (e.g. according to this page http://blog.flexmonkeypatches.com/2008/02/18/simple-datagrid-combobox-as-item-editor-example/) it should be easy, but I can't for the life of me make this work.
The difference I have to the example linked above is that my display value (what the user sees) is different to the id value I want to select on and store in my data provider.
So what I have is:
<mx:DataGridColumn headerText="Type" width="200" dataField="TransactionTypeID" editorDataField="value" textAlign="center" editable="true" rendererIsEditor="true">
<mx:itemRenderer>
<mx:Component>
<mx:ComboBox dataProvider="{parentDocument.transactionTypesData}"/>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
Where transactionTypesData has both 'data' and 'label' fields (as per what the ComboBox - why on earth it doesn't provide both a labelField and idField I'll never know).
Anyway, the above MXML code doesn't work in two ways:
The combo box does not show up with any selected item.
After selecting an item, it does not store back that selected item to the datastore.
So, has anyone got a similar situation working?
While Jeff's answer is a partial answer for one approach for this (see http://flex.gunua.com/?p=119 for a complete example of this being used to good effect), it isn't as general as I wanted.
Thankfully, I finally found some great help on Experts Exchange (the answers by hobbit72) describes how to create a custom component that works in a grid as a ItemRenderer.
I've extended that code to also support using the combo box as an ItemEditor as well. The full component is as follows:
<?xml version="1.0" encoding="utf-8"?>
<mx:ComboBox
xmlns:mx="http://www.adobe.com/2006/mxml"
dataChange="setSelected()"
change="onSelectionChange(event)"
focusEnabled="true">
<mx:Script>
<![CDATA[
import mx.events.DataGridEvent;
import mx.events.ListEvent;
import mx.controls.dataGridClasses.DataGridListData;
private var _ownerData:Object;
private var _lookupField:String = "value";
// When using this component as an itemEditor rather than an itemRenderer
// then set ' editorDataField="selectedItemKey"' on the column to
// ensure that changes to the ComboBox are propogated.
[Bindable] public var selectedItemKey:Object;
public function set lookupField (value:String) : void {
if(value) {
_lookupField = value;
setSelected();
}
}
override public function set data (value:Object) : void {
if(value) {
_ownerData = value;
setSelected();
}
}
override public function get data() : Object {
return _ownerData;
}
private function setSelected() : void {
if (dataProvider && _ownerData) {
var col:DataGridListData = DataGridListData(listData);
for each (var dp:Object in dataProvider) {
if (dp[_lookupField] == _ownerData[col.dataField]) {
selectedItem = dp;
selectedItemKey = _ownerData[col.dataField];
return;
}
}
}
selectedItem = null;
}
private function onSelectionChange (e:ListEvent) : void {
if (selectedItem && _ownerData) {
var col:DataGridListData = DataGridListData(listData);
_ownerData[col.dataField] = selectedItem[_lookupField];
selectedItemKey = selectedItem[_lookupField];
}
}
]]>
</mx:Script>
</mx:ComboBox>
Using this component is straight forward. As an ItemRenderer:
<mx:DataGridColumn headerText="Child" dataField="PersonID" editable="false" textAlign="center">
<mx:itemRenderer>
<mx:Component>
<fx:GridComboBox dataProvider="{parentDocument.childrenData}" labelField="Name" lookupField="PersonID" change="dispatchEvent(new mx.events.DataGridEvent(mx.events.DataGridEvent.ITEM_FOCUS_OUT, true, true))"/>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
Using this component is straight forward. And as an ItemEditor:
<mx:DataGridColumn labelFunction="lookupChildName" headerText="Child" dataField="PersonID" editable="true" editorDataField="selectedItemKey">
<mx:itemEditor>
<mx:Component>
<fx:GridComboBox dataProvider="{parentDocument.childrenData}" labelField="Name" lookupField="PersonID" change="dispatchEvent(new mx.events.DataGridEvent(mx.events.DataGridEvent.ITEM_FOCUS_OUT, true, true))"/>
</mx:Component>
</mx:itemEditor>
</mx:DataGridColumn>
Note that when using it as an ItemEditor, a custom labelFunction (that looks up the Name from the PersonID in my case) must be used, otherwise you only see the key in the grid when the field isn't being edited (not a problem if your keys/values are the same).
Note that in my case, I wanted the item focus out event to propogate up to provide immediate feedback to the user (my DataGrid has itemFocusOut="handleChange()"), hence the change event creating an ITEM_FOCUS_OUT event.
Note that there are probably simpler ways to have a ComboBox as an ItemEditor when you don't mind the ComboBox only shown when the user clicks on the cell to edit. The approach I wanted was a generic way to show a combo box in a DataGrid for all rows, and being editable and with decent event propogation.
The easiest way to add itemRenderers to DataGrids is to make a custom MXML component. In your case make a canvas, HBox, or VBox as the custom component and add the combobox as a child.Set the dataProvider on the dataGrid itself and assign the itemRenderer to the column, and then override the set data function of the itemRenderer to access all data from the given data provider for that instance as seen below:
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
override public function set data(value:Object):void{
trace(value.data);
trace(value.name);
}
]]>
</mx:Script>
<mx:ComboBox width="100%" height="100%" id="myComboBox"/>
</mx:HBox>
This method will be called for each instance of the itemRenderer
In my case I used a spark datagrid where one of the columns has an ItemRenderer that utilises a DropDownListBox. My problem was that when my item list change, the DropDownLists doesn't get updated with the new dataProvider. To solve this, I had to pass the dataProvider for the DropDownListBox as part of the data (of the ItemRenderer), and then by overriding the setter of the data to just assign the DropDownlListBox's dataProvider. Probably a bit of overhead, but if someone have a better solution, please let me know:
<s:GridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
override public function set data(v : Object) : void {
super.data = v;
if (v == null)
return;
dropDown.dataProvider = data.dataProvider;
}
]]>
</fx:Script>
<s:DropDownList id="dropDown" width="100%" height="100%" dataProvider="{data.dataProvider}" labelField="name"/>

How do I ensure a Flex dataProvider processes the data synchronously?

I am using an component, and currently have a dataProvider working that is an ArrayCollection (have a separate question about how to make this an XML file... but I digress).
Variable declaration looks like this:
[Bindable]
private var _dpImageList : ArrayCollection = new ArrayCollection([
{"location" : "path/to/image1.jpg"},
{"location" : "path/to/image2.jpg"},
{"location" : "path/to/image3.jpg"}
]);
I then refer to like this:
<s:List
id="lstImages"
width="100%"
dataProvider="{_dpImageList}"
itemRenderer="path.to.render.ImageRenderer"
skinClass="path.to.skins.ListSkin"
>
<s:layout>
<s:HorizontalLayout gap="2" />
</s:layout>
</s:List>
Currently, it would appear that each item is processed asynchronously.
However, I want them to be processed synchronously.
Reason: I am displaying a list of images, and I want the leftmost one rendered first, followed by the one to its right, and so on.
Edit:
I just found this answer.
Do you think that could be the same issue?
Instead of declaring the variable and using it as the binding source, declare two collections. Then, onCreationComplete call loadNext() which shifts an object out of the second array and pushes it into the first. When the item has been rendered (custom event dispatched by itemRenderer and caught) call loadNext() again until such time as your source array is empty and your bound dataProvider has all the images.
I can write it in code if this doesn't make any sense. ;)
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" minWidth="1024" minHeight="768" creationComplete="init()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var _source : ArrayCollection = new ArrayCollection([
{"location" : "path/to/image1.jpg"},
{"location" : "path/to/image2.jpg"},
{"location" : "path/to/image3.jpg"}
]);
[Bindable] private var dataProvider:ArrayCollection;
protected function init():void
{
this.lstImages.addEventListener( "imageLoaded", handleImageLoaded);
loadImage()
}
protected function loadImage():void
{
if(this._source.length<=0)
return;
var image:Object = this._source.getItemAt(0);
dataProvider.addItem(image);
this._source.removeItemAt(0);
}
protected function handleImageLoaded(event:Event):void
{
loadImage()
}
]]>
</fx:Script>
<s:List
id="lstImages"
width="100%"
dataProvider="{_dpImageList}"
itemRenderer="path.to.render.ImageRenderer"
skinClass="path.to.skins.ListSkin"
>
<s:layout>
<s:HorizontalLayout gap="2" />
</s:layout>
</s:List>
</s:Application>
Your item renderer's image's complete handle will dispatch:
protected function handleImageLoaded(event:Event):void
{
owner.dispatch(new Event("imageLoaded"));
}
And that should load your images in a clean sequence.

Resources