Flex - Checking for change in fields under a tab - apache-flex

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.

Related

Flex TabbedViewNavigator deactivate event in a view

I have a TabbedView application it looks like this:
<s:TabbedViewNavigator width="100%" height="100%" left="0" bottom="0" right="0">
<s:ViewNavigator label="viewA" firstView="views.viewA" width="100%" height="100%"/>
<s:ViewNavigator label="viewB" firstView="views.viewB" width="100%" height="100%"/>
</s:TabbedViewNavigator>
This will create a buttonbar with two buttons at the bottom of the application.
Now I can easily switch between viewA and viewB.
When I define a button in viewA to trigger a CallOut (component in flex) is will appear nice and smooth.
I also created a close button to close the CallOut.
callout.close();
So far so good.
The problem will exist when I use the button to open the CallOut pane in viewA.
Then switch to viewB, the CallOut pane still exists on the screen and did not close.
So I tried these events:
viewDeactivate or/and deactivate
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
viewDeactivate="viewDeactivates()"
deactivate="viewDeactivates()"> <!-- or this -->
But it seems that there is no event when switching between tabs viewA and viewB.
viewDeactivate or deactivate didn't do anything.
I could use a eventlistener on the TabbedViewNavigator and then close the CallOut component if exists in viewA or viewB in the TabbedViewNavigatorView, but this is not a nice way to program, especially when you have multiple CallOut components.
Does anyone know, if there is a good solution for this problem?
Would helped me a lot, thanks in advance!
You need to pass an event argument into the function for it to work!
viewDeactivate="viewDeactivates(event)"
then
private function viewDeactivates(event:Event):void
{
callout.close();
}

Setting focus on a TextField when application starts when there are two states

I have next application header:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()" creationPolicy="all" enterState="focusManager.setFocus(employeeIDTextInput);">
public function init():void {
focusManager.showFocusIndicator = true;
focusManager.showFocus();
focusManager.setFocus(theTextInput);
}
And theTextInput is on the default state. But when application starts theTextField is focused (a blue rectangle is around theTextField) but the cursor isn't inside theTextField. But in the next state I have anotherTextInput and when you switch between states both text inputs are focused correctly as you expect and the cursor appears inside every one of them correctly.
<mx:State name="secondState" enterState="{focusManager.setFocus(anotherTextInput)}">
My question is, Why when application starts the cursor isn't inside theTextInput as commanded on init() function?
Thank you for your answer
I solved it, That problem was because this is a component and is called from a main menu, and the creationComplete event is dispatched just in the moment when the menu is created just before the component appears in screen. What I did was to attach an event handler to show event and that is.
<mx:Canvas width="100%" height="100%" xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="init()" creationPolicy="all"
show="focusManager.setFocus(employeeIDTextInput)"
>
Thank you very much to every one who ask my question...

Trying to use the effectStart and effectEnd from a LIST object in Flex 4.6

I have a mobile app that populates a list. This takes a couple of seconds so I am trying to display the busyindicator. I display the busy indicator when the view is activated and then when the list is complete I want to turn off the busy indicator.
My MXML for the busy indicator and the list declaration is like so:
<s:BusyIndicator id="BI" visible="true" />
<s:List id="lst" effectStart="lstStartHandler(event)" effectEnd="lstFinishHandler(event)" fontSize="20" horizontalCenter="0" textAlign="right" dataProvider="{dp}" useVirtualLayout="true" width="100%" height="100%" top="30" alternatingItemColors="[#66FFFF, #33CCCC]">
My event listeners are like so:
private function lstFinishHandler(event:EffectEvent):void {
BI.visible = false
}
private function lstStartHandler(event:EffectEvent):void {
BI.visible = true
}
My busy indicator always stays on and never goes invisible. It appears the event functions do not execute.
Obviously I am doing something wrong but cannot figure it out. Any ideas would be appreciated.
cheers,
The effectStart and effectEnd properties in MXML are to add event handlers when a Flex effect class is playing an effect on your component.
If you are not triggering any effects on the List, then those event handlers are not going to be executed.
You need to tell the busy indicator to go away by some other means:
dispatch your own event
use data binding
use view states and the currentState property
etc...
You are already using data binding to set the dataProvider for the list, you could simply add another variable and bind to the visible property of the BusyIndicator:
<s:BusyIndicator visible="{isServerResponseComplete}" />
It feels wrong to add a bindable variable (`isServerResponseComplete') just to do this, but it's the simplest answer. Dispatching an event is probably a better approach, but it's difficult to say exactly how you should do it w/out knowing how your app is structured.

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.

Resources