Flex Mobile Spark List *conditional* ItemRenderer - actionscript-3

I want to create a custom spark list which, based on a value (called type) in the data provider row, will set a conditional item renderer for that row.
<s:List>
<s:dataProvider>
<s:ArrayCollection>
<s:source>
<fx:Object type="type1" label="type 1 item" />
<fx:Object type="type2" label="type 2 item" />
<fx:Object type="type3" label="type 3 item" />
<fx:Object type="type2" label="type 2 item" />
<fx:Object type="type4" label="type 4 item" />
</s:source>
</s:ArrayCollection>
</s:dataProvider>
</s:List>
So in essence the item renderer for each type of row would be different.
Why do I want to do this? Because using states in the item renderer for the different layouts is not reliable, it is hit and miss when you scroll quickly.
I did find this code for the mx list:
public class MultipleRenderersList extends List
{
override public function createItemRenderer(data:Object):IListItemRenderer
{
if (data.type == 'type1')
{
return new Type1Component;
}
else if (data.label == 'type2')
{
return new Type2Component;
}
return null;
}
But the spark list doesn't expose the 'createItemRenderer' or even anything similar. It does however have
override public function set itemRenderer(value:IFactory):void
But I have no way of accessing the dataProvider to do the conditional part of the problem.
Can anyone help?

Ok, so found it, I think :/
http://sourceforge.net/adobe/flexsdk/wiki/Spark%20List/
snippet
private function introspectRole(item:Object):IFactory {
if (item.role == "employee") {
return new ClassFactory(EmployeeRenderer);
} else if (item.role == "manager") {
return new ClassFactory(ManagerRenderer);
} else {
return new ClassFactory(DefaultItemRenderer);
}
}
And set the itemRendererFunction to this, apparently simple, but it works, so yeah.
Thanks

Related

Get value of editable GridColumn in flex

I have one datgrid with no. of columns like:
<s:DataGrid id="cpDataGrid" dataProvider="{arrList}">
<s:columns>
<mx:ArrayList>
<mx:source>
<s:GridColumn headerText="Name" dataField="name" editable="false"/>
<s:GridColumn headerText="Age" datafield="age" editable="false"/>
<s:GridColumn headerText="Test" dataField="test" editable="false" />
<s:GridColumn width="100" headerText="Result" dataField="result" >
</mx:source>
</mx:ArrayList>
</s:columns>
</s:DataGrid>
In the above datagrid result column is editable. I have make function when user done entering value in that column as following:
protected function onCreationCompleteHandler(event:FlexEvent):void
{
cpDataGrid.addEventListener(GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_SAVE, onSave);
}
private function onSave(event:GridItemEditorEvent):void
{
var name:String = event.currentTarget.selectedItem.name;
}
Now, I will get name and other field value when edit end. But i need Result text. Means Whatever user entered text in that field.
Like suppose user enter 50 in result column then how can i got that value?
Any help will greatly appreciated.
For spark.DataGrid you need to create custom class based on spark.DataGrid and override method endItemEditorSession. In it you can access DataGrid's property itemEditorInstance.
If you would use mx.DataGrid, you could use DataGridEvent.ITEM_EDIT_END event in combination with itemEditorInstance property of DataGrid.
// register listener
cpDataGrid.addEventListener(DataGridEvent.ITEM_EDIT_END, onItemEditEnd);
...
// listener
private function onItemEditEnd(event:DataGridEvent):void {
// at this point itemEditorInstance is still available, so you can get entered value from it
// if itemEditorInstance is TextInput
var enteredValue:String = TextInput(cpDataGrid.itemEditorInstance).text;
}

Can a button bar only show certain contents of a Viewstack it is provided?

Is it possible for a ButtonBar to only show some of the navigator contents in the viewstack. Like, say I only want the button bar to display the button for the first two, and not the third?
<s:ButtonBar dataProvider="{mainViewStack}"/>
<mx:ViewStack id="mainViewStack">
<s:NavigatorContent>
</s:NavigatorContent>
<s:NavigatorContent>
</s:NavigatorContent>
<s:NavigatorContent>
</s:NavigatorContent>
</mx:ViewStack>
I had similar problem and didn't found native solution. So I wrote next "hack".
Wrote own class that extends NavitagorContent.
ExtNavigatorContent.as
public class ExtNavigationContent extends NavigatorContent
{
public function ExtNavigationContent()
{
super();
}
private var _includeInBar:Boolean = true;
public function get includeInBar():Boolean
{
return _includeInBar;
}
public function set includeInBar(value:Boolean):void
{
_includeInBar = value;
dispatchEvent(new Event("labelChanged"));
}
}
I created new skin for TabBar (in your case it will be ButtonBar). Then, add next in the standard skin (instead of default ButtonBarButton creating):
<s:ButtonBarButton includeInLayout="{data.includeInBar}" visible="{data.includeInBar}" buttonMode="true"/>
And, at last, declared TabBar/ButtonBar:
<s:TabBar id="menu" dataProvider="{viewstack1}" skinClass="design.skins.tabbar.SimpleTabBarSkin"/>
<mx:ViewStack id="viewstack1" width="100%" height="100%">
<components:ExtNavigationContent includeInBar="false" />
<components:ExtNavigationContent includeInBar="true" />
<components:ExtNavigationContent />
</mx:ViewStack>

Flex 4 - How to use filter for NumericStepper?

I have an XML file Which contains Employee details . And I have to use filter on it. Here, I want to filter the experience using two NumericStepper. If I select 1 and 4 for first and second NumericStepper, DataGrid Will display the employee list which are the employee's has experienced between 1 to 4.
Here my code:
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.utils.ObjectUtil;
private function xmlListCollectionFilterFun(item : Object) : Boolean
{
if(employeeName.text.length !=0)
{
if((item.Name).toLowerCase().indexOf(employeeName.text.toLowerCase())!= -1)
{
return true;
}
}
if(employeeID.text.length != 0)
{
if((item.Id).toLowerCase().indexOf(employeeID.text.toLowerCase()) != -1)
{
return true;
}
}
if(endYear.value != 0)
{
if((startYear.value)<=(item.Experience)<=(endYear.value))
{
return true;
}
}
return false;
}
protected function employeeText_changeHandler():void
{
if( employeeName.text.length == 0 && endYear.value == 0 &&
employeeID.text.length == 0 )
{
employeeXMLList.filterFunction = null;
}
else
{
employeeXMLList.filterFunction = xmlListCollectionFilterFun;
}
employeeXMLList.refresh();
}
]]>
</fx:Script>
<fx:Declarations>
<fx:XML id="tempXML"
source="skins/TextXmlFile.xml" />
<s:XMLListCollection id="employeeXMLList"
source="{tempXML.Employee}" filterFunction="xmlListCollectionFilterFun"/>
</fx:Declarations>
<s:layout>
<s:VerticalLayout verticalAlign="top" horizontalAlign="center" paddingTop="30"/>
</s:layout>
<mx:VBox width="100%">
<s:HGroup width="100%">
<s:TextInput id="employeeName" change="employeeText_changeHandler()" prompt="Employee Name"/>
<s:TextInput id="employeeID" prompt="Employee ID" change="employeeText_changeHandler()"/>
<s:NumericStepper id="startYear" minimum="0" maximum="50" snapInterval="1" />
<s:NumericStepper id="endYear" minimum="0" maximum="50" snapInterval="1" change="employeeText_changeHandler()"/>
</s:HGroup>
<s:DataGrid id="dataGrid" dataProvider="{employeeXMLList}" width="100%" height="100%">
<s:columns>
<s:ArrayCollection>
<s:GridColumn id="nameCol" dataField="Name" headerText="Name:"/>
<s:GridColumn id="idCol" dataField="Id" headerText="ID:"/>
<s:GridColumn id="experienceCol" dataField="Experience" headerText="Experience:"/>
</s:ArrayCollection>
</s:columns>
</s:DataGrid>
This is code is no effect for NumericStepper. If anyone can find my mistake?
Try always to split your code in small functions that do a single thing, I would do it like this:
getUserExperience(user:Object):Number
implement this function first do some test and after you get it o return the correct experience then you can go to the next step to make the filter function.
Write some code to compute the min and max experience for the filter
var min:Number=startYear.value;
var max:Number=endYear.value;//if needed adjust this to seome default values
now the filtering should be easy because you just have numbers and no UI components and Objects
so the filter will need to check a simple thing
var userExp:Number=getUserExperience(user);
var response:Boolean=mio<=userExp&&userExp<=max;
return response;
I am sure after you refactor your code to be simple and clear it will be easy to fix any bugs
Is it a typo in your code or a problem with stack overflow that doesn't display some chars (the OR pipes I assume) ?
employeeName.text.length == 0 endYear.value == 0 &&
employeeID.text.length == 0
update: even with pipes, this test does not make sense.

Spark.Components.List with variable content: Flex 4, AS3

I coded the following in Flex 4/AS3 and it doesn't worked as expected.
So, I would like to know if there is something wrong with my code... I'll explain it:
TileTest.mxml
<s:Application minWidth="955" minHeight="600" creationComplete="createButtons()"
<fx:Script>
<![CDATA[
import Object.TestButton;
import mx.collections.ArrayList;
private var _names:Array = ["one", "two", "three"];
public function createButtons():void
{
var buttons:ArrayList = new ArrayList();
for(var a:int = 0; a < _names.length; a++)
{
var testButton:TestButton = new TestButton();
testButton.customName = _names[a];
buttons.addItem(testButton);
}
myList.dataProvider = buttons;
}
]]>
</fx:Script>
<s:VGroup gap="12" width="100%">
<s:Label text="Options" fontSize="18" fontWeight="bold" color="#333333" />
<s:List width="100%" height="100%" id="myList" itemRenderer="Object.TestButton" borderVisible="false">
<s:layout>
<s:TileLayout orientation="rows" columnWidth="290" rowHeight="90" columnAlign="left" horizontalGap="0" verticalGap="0" />
</s:layout>
</s:List>
</s:VGroup>
</s:Application>
This is my Application. Here, I have a List called myList, where I load some TestButtons. I set a name for each button inside the loop.
TestButton
<s:Group width="300" height="90" click="{ Alert.show(_name); }"
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private var _name:String = "just a test...";
public function set customName(newName:String):void
{
_name = newName;
Alert.show(_name);
this.addEventListener(MouseEvent.MOUSE_OVER, function():void{ Alert.show(_name); });
}
]]>
</fx:Script>
<s:BorderContainer accentColor="#000000" width="100%" height="100%" />
</s:Group>
As you can see, I have three Alerts in this component... I did so we can understand the problem.
The first Alert occurs when I set the customName, which occurs in the Application, as already shown.
The second one should occur on Mouse_Over, as the event listener been added to the Group element.
And the third Alert should occur on Click in the Group element.
Now, if I run the resulting swf, I see all the three buttons in the screen and three Alerts, one for each set customName by the Application and it alerts the right name.
If I put the mouse over any button, it doesn't alert any message.
If I click any button, it alerts the default message set to the _name property, which is "just a test..."
I can't understand what is the problem, as I was expecting it to always alert the name set by the Application to each button. Also, I don't want to change the components... what I'm saying is that I would like to have a List and TestButtons with a private String inside.
If the problem is in these specific implementation, so I'll have no other way than change it...
Thank you all!
The problem is that you have a list of TestButtons in your data provider, instead of just plain data. Those buttons get created, and that is why you see the correct Alert messages.
So, then Flex sees a list with three items and therefore it creates three additional TestButtons to render the data in your list. But those TestButtons are completely new ones, with default values for its properties.
To fix this, it would be better if you had data only in your data provider ArrayList, and you would access it through the data property of your item renderer.

Flex: Simulating a click on a button inside an item renderer of a data grid

I'm using a data grid with an item renderer that creates a toggle button. The idea is to have a list of items and only allow one to be selected and pre-select one at start.
I've got the single button selection working, meaning that when I click on on toggle button, the others are deselected.
My problem is to create a way of pre-selecting an element of the data grid or simulate a click on a row and selecting the corresponding toggle button.
If I use the datagrid.selectedIndex the result is a selection but the toggle button doesn't get selected.
Here is the code example
In this example I am using the array value "selected" to define selected button, not my favourite solution but the one that worked first.
The array collection:
public static const ValuesList : ArrayCollection = new ArrayCollection(
[
{ID:0, Name:"One", selected:false},
{ID:1, Name:"Two", selected:false},
{ID:2, Name:"Three", selected:false},
{ID:3, Name:"Four", selected:false}
]
);
The data grid:
<s:DataGrid id="dataGrid" dataProvider={ValuesList} >
<s:columns>
<s:ArrayList>
<s:GridColumn id="GridCol0" />
<s:GridColumn id="GridCol1" />
<s:GridColumn id="GridCol2" itemRenderer = "detail_ItemRenderer" />
</s:ArrayList>
</s:columns>
</s:DataGrid>
The column item renderer:
<?xml version="1.0" encoding="utf-8"?>
<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"
clipAndEnableScrolling="true">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
/**
* Creatioon complete event handler to set toggle button content value.
* */
protected function MyToggleButton_creationCompleteHandler(event:FlexEvent) : void
{
MyListToggleButton.label = data.Name;
MyToggleButton.selected = data.selected;
}
/**
* One of the only function that are called on item interaction.
* */
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth,unscaledHeight);
MyToggleButton.selected = data.selected;
}
]]>
</fx:Script>
<s:ToggleButton id="MyToggleButton" width="100%" height="100%"
creationComplete="MyToggleButton_creationCompleteHandler(event)" />
</s:GridItemRenderer>
SOLUTION:
Using the data array to pass information into the Toggle button. Setting one value to "true" will display the selected element.
To insure data integrity I advise to set the selected index of the grid to the corresponding index of the value set to "true":
public function SetSelectedIndexByName() : int
{
for (var i : int=0; i < dataGrid.dataProvider.length ; i++)
{
if (dataGrid.dataProvider[i].toString().toUpperCase() == "TRUE")
{
return i;
}
}
return -1;
}
In your GridItemRenderer, you should overwrite the setter of data. Your data will contain a variable defining the selection of the ToggleButton. In the setter you will be able to toggle the button based on this variable.
To have only one button selected, you may use a static variable in your renderer which stores the selected itemrenderer. This one is simpler but not really clean since it's a matter of data, not renderers. So you may listen for any change in the dataProvider from a higher level and ensure there's only one item selected.