Flex: use the selectedItem of DateGrid in an function - apache-flex

I'm using a spark Datagrid in an mobile Flex (4.6) application. When a row is selected in the grid I want to trigger a function and use the content of the selected item in that same function. This is my Datagrid
<s:DataGrid id="patientGrid" x="317" y="211" width="393" height="177"
dataProvider="{patientInfo}" gridClick="patientSelect(event)">
<s:columns>
<s:ArrayList>
<s:GridColumn dataField="FirstName" headerText="First Name"/>
<s:GridColumn dataField="LastName" headerText="Last Name"/>
<s:GridColumn dataField="DateOfBirth" headerText="Date Of Birth"/>
<s:GridColumn dataField="Gender" headerText="Gender"/>
</s:ArrayList>
</s:columns>
</s:DataGrid>
And when a item is selected the patientselected function needs the ability to work with the content of that selected item.
I hope my question is clear, and thanks for helping!

Use the GridSelectionEvent.SELECTION_CHANGE event instead for two reasons:
it will provide information on which cells have been selected
it is fired whenever the selection changes (if you only react on mouse clicks, you ignore keyboard navigation/selection)
.
<s:DataGrid id="dg" selectionChange="onSelectionChange(event)" />
private function onSelectionChange(event:GridSelectionEvent):void {
var index:int = event.selectionChange.rowIndex;
var patient = dg.dataProvider.getItemAt(index);
patientSelect(patient);
}

Related

Flex Datagrid Column Total

I have a data grid that that has a title, price, qty and total column. The title, price and qty data is loaded from an xml file and the total is populated with a labelFunction to multiply the price by the qty.
I'm able to populate the total for each row with a labelFunction by returning a string to the row under the total column, but I'm having trouble figuring out how to get the overall total for the total column. I'd like to get the overall total and display it in a textBox/somewhere else outside of the datagrid.
I'm able to get the total by using the updateEsimate function, but it'll only send the total using itemEditEnd on the datagrid (which means i'd have to click on eat qty row for it to tally up) and I'd like it to give me the total automatically once it loads.
Help Please!
(some sample code)
public function updateEstimate(event:DataGridEvent):void
{
// TODO Auto-generated method stub
var sum:Number = 0;
for(var i:int=0; i<orderGrid.dataProvider.length ; i++) {
sum += Number(orderGrid.dataProvider.getItemAt(i).total);
totaltxt.text = sum.toString();
}
totaltxt.text = sum.toString();
}
public function getTotal(item:Object, column:DataGridColumn):String
{
var sum:Number = item.price * item.quantity;
return sum.toString();
}
<mx:XMLListCollection id="xmlProdListColl"
source="{productXML.lastResult.offer}"
/>
</fx:Declarations>
<mx:DataGrid id="orderGrid" x="44" y="0" width="640" height="155"
dataProvider="{xmlProdListColl}"
doubleClickEnabled="true" editable="true"
itemEditEnd="orderGrid_itemEditEndHandler(event); updateEstimate(event)">
<mx:columns>
<mx:DataGridColumn headerText="Title" dataField="title" editable="false"/>
<mx:DataGridColumn headerText="Price" dataField="price" editable="false"/>
<mx:DataGridColumn headerText="Quantity" dataField="quantity"/>
<mx:DataGridColumn headerText="Total" labelFunction="getTotal" editable="false"/>
</mx:columns>
</mx:DataGrid>
<s:RichText id="totaltxt" width="147" height="84" fontSize="18" text="" textAlign="center"
verticalAlign="middle" />
From what i see you calculate the totals in getTotal() to display the totals but are not settings the "total" property in the actual object. And it is the total property that you use in updateEstimate(). So whenever you would edit the quantities you still would see correct total in the datagrid but the value in the textfield would remain the same
I am not a big fan of binded dataProviders because you never know when data is available and it's hard to modify it (like we need it here). I prefer my own dataProvider variables that are strong typed and that I can modify as I wish :)
So I would do it this way:
I assume, your XML looks somethign like this and you don't have the "total" value in it:
<root>
<lastResult>
<offer>
<title>Title</title>
<price>20</price>
<quantity>1</quantity>
</offer>
<offer>
<title>Title 2</title>
<price>30</price>
<quantity>2</quantity>
</offer>
</lastResult>
At some point in your code you will have your XML. This is where you modify it adding a total property and pass the dataProvider to the grid:
private var _orderDataProvider:XMLListCollection;
private function gotData():void
{
var list:XMLList = new XMLList(productXML.lastResult.offer);
_orderDataProvider = new XMLListCollection(list);
updateEstimate(); // call this before we assign the dataprovider to the grid, so we will have totals in items
orderGrid.dataProvider = _orderDataProvider;
}
public function updateEstimate(event:DataGridEvent = null):void
{
// update all totals in all items and the "Estimated total" in one go
var sum:Number = 0;
for (var i:int = 0; i < _orderDataProvider.length; i++)
{
var item:Object = _orderDataProvider.getItemAt(i);
item.total = item.quantity * item.price;
sum += Number(_orderDataProvider.getItemAt(i).total);
}
totaltxt.text = sum.toString();
}
MXML:
<mx:DataGrid id="orderGrid"
x="44"
y="0"
width="640"
height="155"
doubleClickEnabled="true"
editable="true"
itemEditEnd="updateEstimate(event)">
<mx:columns>
<mx:DataGridColumn headerText="Title"
dataField="title"
editable="false"/>
<mx:DataGridColumn headerText="Price"
dataField="price"
editable="false"/>
<mx:DataGridColumn headerText="Quantity"
dataField="quantity"/>
<mx:DataGridColumn headerText="Total"
dataField="total"
editable="false"/>
</mx:columns>
</mx:DataGrid>
<s:RichText id="totaltxt"
width="147"
height="84"
fontSize="18"
text=""
textAlign="center"
verticalAlign="middle"/>
Now as you see this is not the ideal code because we update totals in ALL items every time on edit although you edit only one entry but we don't have to mess with several functions, so as long as you don't have 1000 entries in your list it should be fine.
You can use the CollectionEvent on the XMLListCollection instead. This will get dispatched at the beginning and when any updates are made to the data:
public function updateEstimate(event:CollectionEvent):void{
// your update code
}
<mx:XMLListCollection id="xmlProdListColl"
collectionChange="updateEstimate(event)"
source="{productXML.lastResult.offer}"/>

Flex - Adding a New Line in a DataGridColumn

To start with, I am very new to Flex.
I have a ComboBox that is filled in with choices from the database. Underneath that is a Flex Datagrid with DataGridColumns (mx:located below). I would like to figure out a way that when a selection is made from the comboxbox and the add button is clicked, it populates the next line in the datagrid column based off what was selected. Any thoughts on how this can be done? Maybe I should not use a combobox, just populate the datagridcolumn, not for sure, any hep would be great.
ComboBox Choices - Apples, Oranges, & Pears
Each choice is linked with attributes.
(Apples) nameSpace, countrySpace, infoSpace
(Oranges) nameSpace, countrySpace, infoSpace
(Pears) nameSpace, countrySpace, infoSpace
public var ta1:ArrayCollection = new ArrayCollection();
//Is there a better way of writing this?
public function addDataGridColumn():void
{
var list:ArrayCollection = templateAttributes;
var att:TemplateAttribute = new TemplateAttribute();
(templateProp.dataProvider as ArrayCollection).addItem(att);
}
<mx:HBox>
<mx:ComboBox dataProvider="{templateAttributes}" width="300" prompt="Select a Template Attribute" enabled="{userInEditMode}" labelField="attributeName" />
<mx:Button id="addButton" click="addDataGridColumn();" styleName="addButtonOff" enabled="{userInEditMode}" label="ADD" />
</mx:HBox>
<mx:DataGrid id="templateProp" dataProvider="{templateAttributes}" width="100%" height="100%" editable="true">
<mx:columns>
<mx:DataGridColumn id="nameSpace" dataField="nameSpace" headerText="Name" width="25" editable="{userInEditMode}"/>
<mx:DataGridColumn id="valueSpace" dataField="valueSpace" headerText="Value" width="25" editable="{userInEditMode}" />
<mx:DataGridColumn id="countrySpace" dataField="countrySpace" headerText="Main Country" width="25" editable="{userInEditMode}" />
<mx:DataGridColumn id="infoSpace" dataField="infoSpace" headerText="Information" width="25" editable="false"/>
<mx:DataGridColumn id="infoSpace" dataField="infoSpace" headerText="Information" width="25" editable="false"/>
</mx:columns>
</mx:DataGrid>
Rewriting that function would only shave off a line of code:
public function addRow() : void {
var att:TA= new TA();
att.attributeName = "abc";
(template1.dataProvider as ArrayCollection).addItem(att);
I dunno what TA is, but if you wanted it to be even shorter, you could make TA take attributeName in its constructor. Then you could do this:
public function addRow(attributeName:String) : void {
(template1.dataProvider as ArrayCollection).addItem(new TA(attributeName));
}

Flex Spark Datagrid Cell 1 * Cell 2 stored in Cell 3

I am kinda new with datagrids and spark, and been wracking my brain to figure this out. I have a datagrid being loaded with an XMLList. One field is a numeric value that will be calculated times another field and that result be stored and displayed in the grid on the fly.
Example:
XML
<SampleTable>
<Row>
<Item>Item 1</Item>
<Quantity>10</Quantity>
<Price></Price>
<Cost></Cost>
</Row>
</SampleTable>
So a user would enter in the price, and the Cost would be updated in the grid with the Price * Quantity values, with the dataprovider being updated with the result when the form is saved.
Adding in the grid and the XML is already bound. I can get simple updates to the cells to work. I need help figuring out where to do a calculation. Only the price is editable, and when that cell changes value I want the Cost to be calculated.
Handlers for the editing session:
import spark.components.gridClasses.CellPosition;
import spark.events.GridEvent;
private var mouseDownRowIndex:int;
private var mouseDownColumnIndex:int;
protected function dataGrid_gridMouseDownHandler(event:GridEvent):void
{
mouseDownRowIndex = event.rowIndex;
mouseDownColumnIndex = event.columnIndex;
}
protected function dataGrid_gridMouseUpHandler(event:GridEvent):void
{
// Start a grid item editor if:
// - the rowIndex is valid
// - mouseUp is on the same cell and mouseDown
// - shift and ctrl keys are not down
// - cell is editable
// - an editor is not already running
// An editor may already be running if the cell was already
// selected and the data grid started the editor.
if (event.rowIndex >= 0 &&
event.rowIndex == mouseDownRowIndex &&
event.columnIndex == mouseDownColumnIndex &&
!(event.shiftKey || event.ctrlKey) &&
event.column.editable &&
!event.grid.dataGrid.itemEditorInstance)
{
event.grid.dataGrid.startItemEditorSession(event.rowIndex, event.columnIndex);
}
}
<s:DataGrid id="dgTest" x="10" y="68" width="900" editable="true" electionMode="singleCell" requestedRowCount="4" gridMouseDown="dataGrid_gridMouseDownHandler(event)" gridMouseUp="dataGrid_gridMouseUpHandler(event)">
<s:columns>
<s:ArrayList>
<s:GridColumn width="250" dataField="Item" headerText="Item" resizable="true" sortable="false" editable="false"></s:GridColumn>
<s:GridColumn width="160" dataField="Quantity" headerText="Quantity" resizable="false" sortable="false" editable="false"></s:GridColumn>
<s:GridColumn width="90" dataField="Price" headerText="Price" resizable="false" sortable="false" ></s:GridColumn>
<s:GridColumn width="90" dataField="Cost" headerText="Cost" resizable="false" sortable="false" editable="false"></s:GridColumn>
</s:ArrayList>
</s:columns>
</s:DataGrid>
You should make your datasource Bindable.
So in code (AS), this would be:
[Bindable]
var myXMLList:XMLList;
I don't think it is possible to bind to an XMLList however. You should make use of an XMLListCollection instead.
It's still not clear to me if your want to save your data somewhere, or if you just want to visualize...
s:GridColumn has a property called "labelFunction". Maybe this is sufficient for your needs.
public function myLabelFunction(item:Object, column:DataGridColumn):String
{
var amount:Number = item.Quantiy;
var price:Number = item.Price;
return amount*price as String
}
In you example
<s:GridColumn width="90" dataField="Cost" headerText="Cost" resizable="false" sortable="false" editable="false"></s:GridColumn>
would be changed into
<s:GridColumn width="90" dataField="Cost" headerText="Cost" resizable="false" sortable="false" editable="false" labelFunction="myLabelFunction"></s:GridColumn>
Provided code is by no means complete. It serves as an example.
Good luck.
Got it working using an itemrenderer. It's ugly but works.
What I am having trouble with is moving this event to when the PercentComplete field is changed instead of the click event on this field. This is the timing I am having trouble with.
<s:GridColumn width="90" dataField="Extension" headerText="Extension" resizable="false" sortable="false" rendererIsEditable="true">
<s:itemRenderer>
<fx:Component>
<s:GridItemRenderer mouseDown="itemrenderer1_dataChangeHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import spark.events.GridEvent;
protected function itemrenderer1_dataChangeHandler(event):void
{
if(data){
var data_field:String = ((parseFloat(data['PercentComplete'])/100)*parseFloat(data['Weight'])).toFixed(2).toString();
this.dataLabel.text = data_field;
data.Extension[0] = data_field;
}
}
]]>
</fx:Script>
<s:Label id="dataLabel" text="{data.Extension}" height="100%" width="100%" textAlign="left" verticalAlign="middle"/>
</s:GridItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:GridColumn>
The Datagrid is owner of the datasource, not the GridItemRenderer.
Therefore you should not use mouseDownEvents. (What will happen when to Tab trough your cells?). You might even consider moving this kind of business logic away from your view into your controller (or your model).
For your example, your really don't need a "fancy" GridItemRenderer. Everything has to be calculated outside of your datagrid.
The spark Datagrid has an event which is call "gridItemEditorSessionSave".
spark.components.DataGrid.gridItemEditorSessionSave
Dispatched after the data in item editor has been saved into the data
provider and the editor has been closed.
Event Type: spark.events.GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_SAVE
Language Version: 3.0
Player Version: Flash 10, AIR 2.5
Product Version: Flex 4.5
Personally I would implement a Bindable ValueObject and convert my XMLList in an IList implementation, for example and ArrayCollection, containing a set of those objects. Example follows:
[Bindable]
public class MyValueObject
{
public var percentComplete:Number;
public var weight:Number;
public function get extension():Number
{
return percentComplete*weight;
}
}
If you populate your datagrid with these ValueObjects, you can use a dumb view and even better, you can use any view you like without having to change your actual application. (Isn't that the purpose of OO Programming?)
This question is completely different than the first one though. I'm willing to provide answers, but you should do some research on your own.

customized advanced datagrid in Flex 4

I had 7 columns in an advanced datagrid and have one comboBox with all of the columns names.
The datagrid should show only the columns the use has selected in the comboBox. Does this mean customization of the advanced datagrid columns? If anyone has any samples, please share them.
For example if you have a DataGrid like this
<s:DataGrid
width="100%" height="100%" dataProvider="{ac01}" >
<s:columns>
<s:ArrayList>
<s:GridColumn id="colType" dataField="type" headerText="Type"/>
<s:GridColumn dataField="message" headerText="Message" />
</s:ArrayList>
</s:columns>
</s:DataGrid>
You can trigger some event and call this line of code
colType.visible = false;
Then the column colType will disappear. If you want to show that column again, just assign the true value to its visible property.

Nested datagrid : setting focus on the inside datagrid

I have a datagrid with a single column rendered with another datagrid. I'm doing this to implement a rowspan-like display (with a hbox beneath the child datagrid) showing messages under each row.
When I tab and reach the end of a row, I want the focus to pass to the next row i.e the next child datagrid and on a specific cell of that row.
This is the simplified code calling the renderer :
<mx:DataGrid width="100%"
showHeaders="false"
selectable="false"
id="ParentDatagrid"
dataProvider="{arrayActs}"
paddingBottom="0" paddingTop="0"
variableRowHeight="true">
<mx:columns>
<mx:DataGridColumn itemRenderer="components.ColumnRendererDatagrid"/>
</mx:columns>
</mx:DataGrid>
And the renderer (ColumnRendererDatagrid) code :
<mx:DataGrid
id="dgLocal" width="100%" height="23" borderSides=""
dataProvider="{data}" showHeaders="false"
editable="true" selectable="false">
<mx:columns>
<mx:DataGridColumn />
<mx:DataGridColumn />
<mx:DataGridColumn />
<mx:DataGridColumn />
<mx:DataGridColumn />
</mx:columns>
</mx:DataGrid>
<mx:HRule width="100%" />
<mx:Label id="message" text="Error Message" width="100%" />
For the moment, I'm using the following snippet in ColumnRendererDatagrid to check when tabbing reaches the end of the row and bubble up the event :
if(dgLocal.editedItemPosition.columnIndex == 13){
dispatchEvent(new Event(MOVE_FOCUS_DOWN, true));
From there I'm struggling on how to drill down into the renderer to set the focus once the higher component get this event. Any help would be really appreciated. Thx
Ok, here is the solution I came up with. This is the code in the parent's event handler (handling the MOVE_FOCUS_DOWN):
//Find the postition of the item that sent the event :
for each( var row:Object in ParentDatagrid.dataProvider ) {
if( (event.target as ColumnRendererDatagrid).data == row) {
break;
}
i++;
}
//Get the renderer of the next item :
var render:IListItemRenderer = ParentDatagrid.itemToItemRenderer(arrayActes.getItemAt(i+1));
(render as ColumnRendererDatagrid).dgLocal.editedItemPosition = {rowIndex:0, columnIndex:1}
(obviously checks should be made in real code to see if the next object exists) which is of type ColumnRendererDatagrid. From there I just set the focus/editing position.

Resources