keep my sort after updating the dataprovider's data - apache-flex

I have a datagrid where the data in the dataprovider changes every 30 seconds automatically. Everytime it does, if I chose to sort the column by clicking on the header, it will revert back to the default sort (before I clicked on the header). I want to know how I can control the sorting so that if I had clicked on it previously, it will remain sorted as so the next 30 seconds.
<mx:DataGrid id="id" width="100%" height="100%">
<mx:columns>
<mx:DataGridColumn dataField="#col1" headerText="Type1" itemRenderer="itemRenderer" />
<mx:DataGridColumn dataField="#col2" headerText="Type2" itemRenderer="itemRenderer" />
</mx:columns>
<mx:dataProvider>
{xmllist_extractedfromAnotherSourceEvery30Seconds}
</mx:dataProvider>
</mx:DataGrid>

A guess... The sort order is stored within the dataProvider (ArrayCollection), and it gets lost when you re-assign a new dataProvider.
You could either:
update the dataProvider using ArrayCollection update functions,
instead of re-assigning it
if not possible, get the ISort from the dataProvider before changing it; then apply this ISort to the new dataProvider after
loading (and remember to refresh() the dataProvider)

Related

Add event listener to Flex components inside a repeater

I want to add an event listener to each component inside a repeater, but don't know how. Here's some code I have tried:
<mx:Repeater id="rp" dataProvider="{dataProvider}" width="100%">
<mx:Button id="attach" creationComplete="addListeners(attach[rp.currentIndex])"/>
</mx:Repeater>
This doesn't work. creationComplete is not called until the repeater has finished instead of (as I expected) when the creation of the button is complete. I'm not sure how to accomplish this.
BTW - I also tried placing the creationComplete on the parent component to the repeater, but it would only be called the first time the component was rendered (the data inside the repeater sometimes changes) so that didn't work.
If you're using an Event defined in metadata, it can be as simple as adding click="myClickHandler(event)". You can also do something like this:
<yourNS:YourComponent id="foo>
<creationComplete>
<fx:Script>
(foo[yourRepeater.currentIndex] as EventDispatcher).addEventListener('the event', yourEventHandler);
</fx:Script>
</creationComplete>
</yourNS:YourComponent >

Problem getting tooltip to refresh properly on an itemrenderer in Flex

I'm having the following problem.
I have an ArrayCollection that's acting as the data provider for a tilelist (called favoriteLinksList)
I use an itemRenderer called FavoriteItem as the tilelist's itemRenderer. This FavoriteItem looks like this:
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
width="280" height="163"
horizontalAlign="center"
paddingLeft="5" paddingRight="5" paddingTop="0" paddingBottom="0" xmlns:ns1="*">
<mx:Canvas width="100%" height="100%">
<mx:Image
id="thumbnail"
width="178" height="115"
source="{data.thumbnail}"
toolTip = "{data.tooltip}" x="46" y="10"/>
<mx:Text
id="title"
text="{data.tileListTitle}"
width="254"
toolTip="{data.tooltip}" x="10" y="133"/>
</mx:Canvas>
</mx:VBox>
As you can see, the tooltips for the two items in it are taken from data.tooltip
This works fine.
The problem is refreshing the tooltip when it has changed.
The objects (of type Object) in the ArrayCollection each have a property called tooltip (obviously since that's where the itemRenderer is getting its info from).
When I change this property to its new value, the tooltip of the itemRenderer doesn't change to reflect this.
I tried to set it manually by getting the itemRenderer from the event that is triggered upon clicking one of the items in the tilelist but without success.
Example:
event.itemRenderer.title.toolTip = event.currentTarget.selectedItem.tooltip;
after having updated the tooltip but this gives a compilation error:
Access of possibly undefined property title through a reference with static type mx.controls.listClasses:IListItemRenderer.
I also tried performing a refresh() on the favoriteLinksList array collection but this gave mixed results. The tooltip was updated correctly but one of the items (the first one) in the tilelist went missing! This seems to be a Flex bug. The data provider has the same number of elements before and after the refresh and this doesn't happen if I click on the first element in the tilelist.
All help is greatly appreciated.
Found a solution to my problem.
The favoriteLinksList is bindable and set as the dataProvider of the tileList. However, changes to the individual objects were not being propagated to the itemRenderer.
I thought that there must a change to the favoriteLinksList Array Collection itself.
As mentioned in my question, I already tried using favoriteLinksList.refresh() but this made the first element in the tileList vanish (though it still seemed to be in the Array Collection). A possible bug in Flex perhaps?
Anyway, discovered that a way around this was to perform the following:
favoriteLinksList.setItemAt(favoriteObject, favoriteLinksList.getItemIndex(favoriteObject));
Essentially, I'm setting the item at index X to itself so not actually doing anything but this is enough for the itemRenderer to refresh the data for the itemRenderer.
I would go about doing 2 things
that the object is actually bindable and the change is happening and getting to the item renderer
possible solution => override the setter for the data property in the item renderer, do not forget to call super.data = value
-
override public function set data(value:Object):void
{
super.data = value;
title.toolTip = data.tooltip;
}
stand with a breakpoint in this row, you should be getting to it when the data changes.

ability to add button\icon in column header of flex datagrid?

I want to display the grid control in Flex ( version 3 )
1. with a marker/(or)icon (which acts like a button) in each colum header of the grid.
2. on click of the button i want o popup a textaread to capture some comments.
3. on close of the popup i wan to then change the marker\icon in a way highliting it which would indicate that some comments(footnotes) are present for this column.
I am very new to flex looking at the data grid control at this point. I understand that the standard features can be easily plugged in by msxml. Do i need to write some complex action script for above feature listed?
If you have experience with any such or related encounter with flex grid, please provide some inputs it will be of great help.thanks.
Here is one of the way
<mx:DataGridColumn dataField="desc">
<mx:headerRenderer>
<mx:Component>
<mx:Image toolTip="This is a column with an image " source="#Embed(source='IconInHeader.png')" verticalAlign="middle" horizontalAlign="center"/>
</mx:Component>
</mx:headerRenderer>
</mx:DataGridColumn>
</mx:columns>
but there are othere ways also from http://softologia.com/node/25
Just add sortItemRenderer="{null}" in your AdvanceDataGrid tag and will get rid of sorting icon(like a verticle line)in your AdvanceDataGrid header.

Flex ItemRenderer as a field of `data`?

I would like to let the data provided to a DataGrid decide how best it should be rendered (that is, let the data carry with it an object which will do the rendering).
For example, by creating a "Renderable" interface, which has a 'renderer:IFactory' property, then used as below:
<mx:DataGrid x="0" y="0" width="100%" dataProvider="{myDataProvider}">
<mx:columns>
<mx:DataGridColumn headerText="Task" width="100"
itemRenderer="{(data as Renderable).renderer}"/>
</mx:columns>
</mx:DataGrid>
But to do this, Renderable has to extend IEventDispatcher, which seems like a little much...
I've also tried using:
itemRenderer="{(data as Renderable).getRenderer()}"
Which does nothing (in fact, the getRenderer method never gets called).
Is there a better way to do this? Am I doing something fundamentally wrong?
Thanks!
I could be wrong but I think the "data" property you're referencing in the code sample above is the "data" for the top-level Container in your view and not for that particular row of the DataGrid. A few other approaches come to mind:
Implement a single item renderer class that examines the data being passed to it and utilizes the proper item renderer for the type of data supplied.
Implement a function in the view of your DataGrid that examines your dataProvider and returns the proper item renderer class; call this inside the DataGridColumn.itemRenderer property using a binding expression.
Implement an subclass of DataGridColumn that has the logic baked into it to set the correct itemRenderer.
I would actually recommend against mixing "renderer data" that is View-specific with data from your Model. Unless you wrap the core model data with an object that exposes it along with the renderer (what some people call a ViewModel).
Make getRenderer a [Bindable] property

Flex - Checking for change in fields under a tab

I'm developing a flex application with 4 tabs. When the user switches a tab I want to reset the previous tab to its initial state. Also I need to alert the user, if he hasn't saved the changes he made if any, will be lost.
I'm planning to set a variable in the Model, and set/reset it if any change happens in a field under a tab. But how do I monitor this? Is there any listener available for this?
Also how do I check and reset the state of the previous tab? The contents that come under the tab is from components only.
[EDIT]
My questions are:
How do I check if the user has made any edits in the current tab? Some fields are generated dynamically too.
I'm calling a function in the onchange event of TabNavigator and asks the user if he really want to switch the tab.I want the other tab to load its contents only if the user clicks Yes to the Alert box I'm popping up. But now the confirmation box pops up, and the contents are loaded into the other tab and if the user clicks No it goes back to the other tab. How do I prevent the action of loading the contents of the other tab at all till the user presses Yes?
Please provide your valuable inputs.
Answer to question 1 is as follows;
Use a Boolean variable to track if a user has edited data. When the user selects a tab set this variable to false. Listen for the change event on all fields within the tab. Set the change event handler for all fields to be a method which sets the Boolean to true. For the dynamic fields, add the same change event handler that the other fields have. Do this as soon as you create each dynamic field. See the code below;
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
private var userChangedData:Boolean=false
function onUserChangedData()
{
trace("onUserChangedData")
userChangedData=true
}
function onTabChanged()
{
trace("ontabchanged")
trace(userChangedData)
userChangedData=false
}
]]>
</mx:Script>
<mx:Panel title="TabNavigator Container Example"
height="90%"
width="90%"
paddingTop="10"
paddingLeft="10"
paddingRight="10"
paddingBottom="10">
<mx:TabNavigator id="tn"
width="100%"
height="100%"
change="onTabChanged()">
<!-- Define each panel using a VBox container. -->
<mx:VBox label="Panel 1">
<mx:Label text="TabNavigator container panel 1"/>
<mx:TextInput text="default"
change="onUserChangedData()"/>
<mx:CheckBox label="check something"
change="onUserChangedData()"/>
</mx:VBox>
<mx:VBox label="Panel 2">
<mx:Label text="TabNavigator container panel 2"/>
</mx:VBox>
<mx:VBox label="Panel 3">
<mx:Label text="TabNavigator container panel 3"/>
</mx:VBox>
</mx:TabNavigator>
</mx:Panel>
For 1) you could dispatch an Event every time a user edits a field. The event can be handled by a command which will update some properties in your model with the right info about what got updated. Then whoever cares in your view can bind to those properties.
For 2) in the onChange() handler call event.preventDefault(). Then you can programatically select the next tab only if the user clicks Yes.
I don't have the reputation to add comments yet, but to answer your question:
"Is it possible to add a global onchange/onkeypress method that hooks to the complete application and sets the boolean? Otherwise I'll have to edit at multiple places to add the onchange event. – Basani"
Yes, have each place that needs to signal that "something changed" dispatch an Event. Then have a Command watching for dispatches of that event. That Command can do all the processing you need, including setting the userDataChanged boolean in the model.
It sounds like you're using Cairngorm based on how you tagged the question, so this should be easily supported.

Resources