use the same validator on several controls in Flex - apache-flex

Say I have a phone-number validator in flex and I have two TextInput controls for phone numbers. I don't want to have two separate validator controls defined that have essentially the same attributes... but each validator has only one "source" attribute. How can I use the same validator on multiple control? (or any equivalent solution)

Not inline, but you can perform the validation programmatically, say, on submission of a form, or when a user tabs out of a control, etc. Here's an example using a single PhoneNumberValidator to validate two form fields; the validation happens when the Submit button gets clicked:
<mx:Script>
<![CDATA[
private function validatePhoneNumber(txt:TextInput):void
{
v.listener = txt;
v.validate(txt.text);
}
private function btn_click():void
{
validatePhoneNumber(p1);
validatePhoneNumber(p2);
}
]]>
</mx:Script>
<mx:PhoneNumberValidator id="v" allowedFormatChars="()- .+" property="text" requiredFieldError="Required." wrongLengthError="Invalid length." invalidCharError="Invalid character." />
<mx:Form>
<mx:FormItem label="Phone Number 1">
<mx:TextInput id="p1" />
</mx:FormItem>
<mx:FormItem label="Phone Number 2">
<mx:TextInput id="p2" />
</mx:FormItem>
<mx:FormItem>
<mx:Button id="btn" label="Submit" click="btn_click()" />
</mx:FormItem>
</mx:Form>
Hope it helps!

Steps to reproduce:
TextInput creates dynamically
textInputBox = new MyTextInput;
textInputBox.restrict = “0-9.”;
textInputBox.maxChars = 24;
amountValidator = new NumberValidator();
amountValidator.source = textInputBox;
amountValidator.property = “text”;
amountValidator.allowNegative = false;
amountValidator.domain = “real”;
amountValidator.precision = 4;
amountValidator.required = false;
amountValidator.maxValue = 999999999999.9999;
amountValidator.trigger = textInputBox;
amountValidator.triggerEvent = Event.CHANGE;
amountValidator.addEventListener(ValidationResultEvent.VALID, amountValid);
amountValidator.addEventListener(ValidationResultEvent.INVALID, amountInvalid);
private function amountValid(event:ValidationResultEvent):void
{
valid = true;
fieldsValidated = true;
}
private function amountInvalid(event:ValidationResultEvent):void
{
valid = false;
fieldsValidated = true;
}
As mention in the creation, when we exceed the limit, it shows error my red color border, and the same time if you delete them by DEL key when it come to the given acceptable limit, automatically become to green soon.
Leave from the field and change values of another textinput(this is just a textinput, this is a form there are some more form elemets), then come back to value exceeded textfield by SHIFT+TABS and remove the additional entered numbers, when you come to green soon your value is accepted.
Now again enter more values and now you are in the warn zone, then leave from the field and do the few changes in other form elements.
Then come back to the value exceeded text filed by MOUSE CLICK, and start delete from DEL, even though you removed additional values, still fields shows that you are in warn zone.
Actual Results:
Even when remove additional numbers,still field is Red
Expected Results:
if remove additional numbers, field should come its normal status.
Picture of this issue can be viewed at View Screen Shot

Related

Flex 3: Keeping focus on a textbox when clicking a button

Quick background. I have a series of editable text areas in which I want to be able to add a variable on the click of a button.
If I click the button the textarea loses focus and so I cant code which textarea I need to add the variable into. Can anyone think of a way of keeping focus on the textbox, inserting the variable and then allowing the user to carry on typing.
I'm not sure if this is part or fully possible but any help would be much appreciated. I've been playing around with the setFocus function trying to get this to work with no success.
Here is a snippet of my code:
public function addFirstName(myText:string):void{
myText = myText + "<<firstname>>";
}
<mx:TextArea id="txt1" change="text1=txt1.text" text="{text3}" editable="true"/>
<mx:TextArea id="txt2" change="text2=txt2.text" text="{text2}" editable="true"/>
<mx:Button label="Insert First Name" click="addFirstName(focusedtextarea)"/>
its the focusedtextarea part Im stuck on
Thanks in advance!
Write some code using the focus out event to store which text area needs to be changed. Conceptually something like this:
var textAreaToBeChanged : TextArea;
protected function onTextAreaLoseFocus(event:FocusEvent):void{
// I'm pretty sure Target is the right property to use here; but didn't test
textAreaToBeChanged = target;
}
Later in your MXML, add the event listener.:
<mx:TextArea id="txt1" change="text1=txt1.text" text="{text3}" editable="true" focusOut="{onTextAreaLoseFocus(event)}"/>
<mx:TextArea id="txt2" change="text2=txt2.text" text="{text2}" editable="true" focusOut="{onTextAreaLoseFocus(event)}"/>
Sorted!
public var textAreaToBeChanged : Object;
public var textposition:int
//when leaving focus on a textbox, record the textarea and text position. If a button is clicked to add a variable, it needs to be added at this position
protected function onTextAreaLoseFocus(event:FocusEvent):void{
textAreaToBeChanged = event.target;
textposition = textAreaToBeChanged.caretIndex;
}
//split the text from the recent textbox at the position the cursor has just been. The restructure the text with the firstname variable in the middle.
public function addFirstName():void{
var firstbit:String = textAreaToBeChanged.text.substr(0,textposition);
var myString:String = firstbit;
myString = myString + firstnameVar;
var lastbit:String = textAreaToBeChanged.text.substr(textposition);
myString = myString + lastbit;
textAreaToBeChanged.text = myString;
//set the focus back to the textarea.
textAreaToBeChanged.setFocus();
//place the cursor after the variable we just added.
textAreaToBeChanged.setSelection(textposition + firstnameVar.length, textposition + firstnameVar.length);
}
and the MXML:
<mx:TextArea id="txt1" change="text1=txt1.text" text="{text3}" editable="true" focusOut="{onTextAreaLoseFocus(event)}"/>
<mx:TextArea id="txt2" change="text2=txt2.text" text="{text2}" editable="true" focusOut="{onTextAreaLoseFocus(event)}"/>
<mx:Button label="Insert First Name" click="addFirstName()"/>

Custom dateField component with comboBox and clear button inside the dropdown calendar in flex3

I inserted a dateField component. On clicking, it displays calendar. I would like to add 2 comboboxes, one shows hours (0 to 23) and other for minutes (0 to 59), to the calendar so that the user can select the time along with the date, and that will be displayed in the text input as date and time. One more thing I would like to add is clear button to clear the selected date in the calendar.
Since a DateField is essentially a TextInput coupled with a DateChooser, why not do that yourself? Also add your two ComboBox, make your TextInput editable="false" text="{dateTime}" where dateTime is a Bindable string variable you create as the concatenation of the values in the DateChooser and the two ComboBox. Call the function that creates the dateTime string on the change event of all three inputs.
Finally, add your clear Button, and have its click event call a function where you set the DateChooser to today, and the two combo boxes to default values, and update dateTime string as needed (to blank or to current date/time depending on what you are trying to do.)
Edit:
As you asked nicely, and as I am studying for the ACE exam and thought this made a nice excercise, here is what I came up with. I made the following custom component in a package named 'components' and called it 'myCustomDateField.mxml'
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml"
initialize="resetDisplay()">
<mx:DateFormatter id="dateFormat" formatString="DD-MMM-YYYY JJ:NN" />
<mx:Script>
<![CDATA[
[Bindable]
public var dateTime:Date;
private function updateDisplay():void {
var userDate:Date = new Date();
userDate.date = cal.selectedDate.date;
userDate.month = cal.selectedDate.month;
userDate.fullYear = cal.selectedDate.fullYear;
userDate.hours = hour.value;
userDate.minutes = minute.value;
dateTime = userDate;
}
private function resetDisplay():void {
var now:Date = new Date();
cal.selectedDate = now;
hour.value = now.hours;
minute.value = now.minutes;
dateTime = now;
}
]]>
</mx:Script>
<mx:Label text="Select Date and Time:" />
<mx:TextInput id="display" text="{dateFormat.format(dateTime)}" editable="false" />
<mx:DateChooser id="cal" yearNavigationEnabled="true" change="updateDisplay()" />
<mx:Label text="Hour:" />
<mx:NumericStepper id="hour" minimum="0" maximum="23" change="updateDisplay()" />
<mx:Label text="Minute:" />
<mx:NumericStepper id="minute" minimum="0" maximum="59" change="updateDisplay()" />
<mx:Button label="Clear" click="resetDisplay()" />
</mx:HBox>
In my application, I added the xmlns:cust="components.*" in the declaration tag, and inserted one <cust:myCustomDateFeild id="myDate" />. I was able to access the entry in the parent by using {myDate.dateTime}.
I made a few design assumption that you may decide to change, like the formatter, and I replaced your combo boxes with NumericStepper.

Updating external Flex components from an action

I'm new to Flex and am having trouble understanding Events. I think Events are what I want to use for my situation. I have 2 components, addUser.mxml and listUsers.mxml. I access these from a ViewStack in my main application. When I load listUsers.mxml it shows a list of current users in a datagrid via a HTTPService call. When I add a user using the form on addUser.mxml I would like the datagrid in listUsers.mxml to refresh when I return to that view to show the new user. I've tried several different things with addEventListener and dispatchEvent but can't seem to get it working. Can someone help me with this logic?
--
Sample code for comment, I've parsed out the non-relative stuff.
adduser look like this:
<mx:HTTPService id="httpService"
url="{'http://someurl.com'}"
useProxy="false"
method="POST"
fault="faultHandler()"
result="resultHandler(event)"
/>
public function addUser():void{
if(validateForm()){
params = {};
params['action'] = 'adduser';
params['firstName'] = firstName.text;
params['lastName'] = lastName.text;
params['email'] = email.text;
params['isActive'] = isActive.selected;
httpService.cancel();
httpService.addEventListener("result", addUserResult);
httpService.send(params);
}
}
public function addUserResult(event:ResultEvent):void{
var result:Object = event.result;
//reset fields if add user was successful
if(result.returnXML.action=='adduser'){
var m:String = result.returnXML.message.toString();
if(result.returnXML.status=='fail'){
Alert.show(m, null, Alert.OK, null, null, Application.application.IconError);
}
if(result.returnXML.status=='success'){
firstName.text = "";
lastName.text = "";
email.text = "";
isActive.selected = true;
Alert.show(m, null, Alert.OK, null, null, Application.application.IconSuccess);
}
}
}
<mx:Button id="addButton" label="Add" click="addUser();" />
listUsers looks like this:
<mx:HTTPService id="httpListService"
url="{'http://someurl.com'}"
useProxy="false"
method="POST"
fault="faultHandler()"
result="resultHandler(event)"
/>
<mx:DataGrid id="dgUsers"
itemClick="dgClickEvent(event);"
width="85%"
maxHeight="500"
>
<mx:columns>
<mx:DataGridColumn headerText="First Name" dataField="fn" />
<mx:DataGridColumn headerText="Last Name" dataField="ln" />
<mx:DataGridColumn headerText="Email" dataField="email" />
<mx:DataGridColumn headerText="Active" dataField="active" />
</mx:columns>
</mx:DataGrid>
I don't think event listeners are necessarily the way to go. You use an event listener to do something upon detection of something else. ie) listening for a mouse down on a ui component = detect mouse down, do drag operation.
Given your example I think you just need to chain your functions together. It looks like your addUser function saves the user to the same source as your list users gets data from, so in your position I would call the listUsers httpService at the end of the add user result to refresh your data populating the datagrid.
httpListService.send()
I don't see your result handler for httpListService but that's where you update the data in your dataGrid.
good luck, and please post back with any complications.
Got it working. Here's what i did - basically everytime the parent viewstack brings the listUsers view into focus, it sends the httpListService and refreshes the datagrid regardless of any events (or non events) in the addUser component or any other component. it adds to the network traffic but, for the scope of my project, that is acceptable.
in listUsers.mxml:
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()">
...
public function init():void{
//vsUsers is my view stack on the main application component
Application.application.vsUsers.addEventListener(IndexChangedEvent.CHANGE, refreshUsersGrid);
}
...
public function refreshUsersGrid(e:IndexChangedEvent):void{
//if the new viewable child is the list users view, then refresh the datagrid
if(Application.application.vsUsers.getChildAt(e.newIndex).name=='viewListUsers'){
//the sendListUsersRequest method fires the HTTPService send method and binds the results to the datagrid
sendListUsersRequest();
}
}

Flex AS3: ComboBox set visible to false doesn't hide

I have a combobox in a view that receives information about application state changes, and then is supposed to show or hide it's children based on the whole application state.
It receives state change messages, it traces the correct values, it does what it's supposed to do, however, it just doesn't seem to work. Essentially, all it needs to do is hide a combobox during one state, and show it again during another state.
Here is the code:
public function updateState(event:* = null):void {
trace("Project Panel Updating State");
switch(ApplicationData.getSelf().currentState) {
case 'login':
this.visible = false;
break;
case 'grid':
this.visible = true;
listProjects.includeInLayout = false;
listProjects.visible = false;
trace("ListProjects: " + listProjects.visible);
listLang.visible = true;
break;
default:
break;
}
}
Here is the MXML:
<mx:HBox>
<mx:Button id="btnLoad" x="422" y="84" label="Load" enabled="true" click="loadProject();"/>
<mx:ComboBox id="listProjects"
x="652"
y="85"
editable="true"
change="listChange()"
color="#050CA8"
fontFamily="Arial" />
<mx:Label x="480" y="86" text="Language:" id="label3" fontFamily="Arial" />
<mx:ComboBox id="listLang"
x="537"
y="84"
editable="true"
dataProvider="{langList}"
color="#050CA8"
fontFamily="Arial"
width="107"
change="listLangChange(event)"/>
<mx:CheckBox x="830" y="84" label="Languages in English" id="langCheckbox" click='toggleLang()'/>
</mx:HBox>
It's not that clear form your code where and how the updateState function gets called, and to get any further into a solution I think I would need to see that. However, I think you may like to consider a different approach.
Have you tried using views instead of manually showing and hiding things and setting properties? I think you would have simpler code if you had a different view state for each of the cases in your switch, e.g. 'login' etc. Then all the showing hiding stuff becomes a design-time activity rather than run-time and all you have to do is set the current state.
If you matched your state names with your ApplicationData currentState values you may even be able to do away with the updateState function completely.
Have you tried changing
updateState(event:* = null):void
to this
updateState(event:Event = null):void
Im still looking into the event:* and everything I have found so far has Event instead of *. Will repost still looking

Preventing validator from firing after bound value changes

I am working on a product options form whose contents should be cleared by a clearOptions() method each time the user adds an option. The option name TextInput is bound to a value object that is reinstantiated each time the the user adds a product option. The problem I am having is that the StringValidator fires each time the bound value object is reinstantiated.
<mx:StringValidator id="valOptionName" minLength="1" source="{txtOptionName}" property="text" trigger="{btnAddChangeOption}" triggerEvent="click"/>
<mx:TextInput id="txtOptionName" width="120" text="{currentProductOption.name}"/>
<mx:LinkButton id="btnAddChangeOption" label="Add/Change Option" click="saveUpdateOption(event)" horizontalCenter="0"/>
The following code is what causes the StringValidator to fire when it shouldn't. It results in a red outline around a text box.
private function clearOptions():void
{
currentProductOption = new ProductOptionVO();
}
Thank you for any help,
Orville
I solved the problem by setting the source of the validator manually in the validation code and then switching it off before currentProductOption is reinstantiated.

Resources