How to remove validation programmatically from flex component
This is my method
public static function validateRequired(txt:TextInput, errorMessage:String="This field is required"):Boolean
{
var v:Validator = new Validator();
v.listener = txt;
var result:ValidationResultEvent = v.validate(txt.text);
var returnResult:Boolean = (result.type == ValidationResultEvent.VALID);
//Alert.show("validation result is " + returnResult);
if (!returnResult) {
v.requiredFieldError = errorMessage;
}
return returnResult;
}
But, as each time i am creating new validator, so pop-up contains multiple messages like
This field is required.
This field is required.
How to remove error messages attached with component?
I had the same problem, I understood that i had to clear the last validation before the next one.
private function resetValidationWarnings():void {
for each (var validator:Validator in arrValidators) {
validator.dispatchEvent(new ValidationResultEvent(ValidationResultEvent.VALID));
}
}
this is a kinda POG but it got the job done!
hope it helps !
The Validator.enabled property lets you enable and disable a validator. When the value of the enabled property is true, the validator is enabled; when the value is false, the validator is disabled. When a validator is disabled, it dispatches no events, and the validate() method returns null.
For example, you can set the enabled property by using data binding, as the following code shows:
<?xml version="1.0"?>
<!-- validators\EnableVal.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:ZipCodeValidator id="zcVal"
source="{inputA}"
property="text"
required="true"
enabled="{enableV.selected}"/>
<mx:TextInput id="inputA"/>
<mx:TextInput/>
<mx:CheckBox id="enableV"
label="Validate input?"/>
</mx:Application>
I also encountered similar problem. In my case, the root cause is I created the validator object every time the validation is called (just as you did). As a result, the UIComponent see it as different validator object (see UIComponent.errorObjectArray) and stored the error message again. The solution is to have a global or static validator and it solves the duplicate error message for me.
Related
well, I have a combobox which I have bind his selectedItem property to a value object object, like this
<fx:Binding source="styles_cb.selectedItem.toString()" destination="_uniform.style"/>
<fx:Declarations>
<fx:XML id="config_xml" xmlns="" source="config.xml" />
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<mx:ComboBox x="66.15" y="63.85" editable="false" id="styles_cb" dataProvider="{config_xml.styles.style}" />
the value object is a custom class with some setters and getters, and I want to set a property based of the value of the selectedItem of the combo, so inside the value object I have something like this
[Bindable]
public function set style(value:String):void
{
_style = value;
trace(value);
}
my problem is that each time I change the combobox selection which in fact change the style property of the value object it does it 3 times, if I trace the value of the setter it actually do the trace 3 times, why?? how can I avoid this? I'm doing something wrong? or there is a better way to do it, please help, thanks for any help
It's common for data binding expressions to fire many times more than one would expect. I don't know the exact reason. If this causes issue for your app, then don't bind the source directly to the target, instead use invalidation. Bind the source to set a flag, stylesSelectedItemChanged and call invalidateProperties(). Then override commitProperties() and inside your commitProperties(), check if stylesSelectedItemChanged is true, and if so, propagate the new value forward to the destination and reset the flag to false. Be sure to also call super.commitProperties() or else many things would break.
Invalidation is extremely common in the Flex framework, every component uses it internally, and it helps a lot with these kinds of issues.
wow!!, some times writing a question let you think about it twice and let you find the answer by yourself, so I find my own solution, in the documentation said I can make all the properties of an object bindables if I put [Bindable] in the class declaration, so I did it like this
[Bindable]
[RemoteClass(alias='Uniform')]
public class Uniform extends Object implements IEventDispatcher
however when I was trying to dispatch an event in the setters I found in the docs that I must add the event name like this
[Bindable("styleChanged")]
public function get style():String
{
return _style;
}
public function set style(value:String):void
{
_style = value;
dispatchEvent(new Event("styleChanged"));
}
now I found that doing this, mark the property with a double bind and that was making me set the property many times, hugg!, but now I know I can avoid using the second [Bindable] and still the event get dispatch, so now I wonder why I need to use [Bindable("styleChanged")] in the first place if I still can dispacth the event with only [Bindable] and the dispatch method?, weird
hope this help to someone
I have got two labels in my flex mxml component.
first one shows playheadtime of videodisplay and another is also used for same purpose. the difference is that when we are in add mode(decided from a flag variable) both should show current playheadtime using binding. but when we are in edit mode(again decided from flag) the latter label should remain static, to be more specific, the value retrived from database.
how can I do that using actionscript. I tried ChangeWathcer but I found it a bit tricky. Is there any other simpler way or am I missing something.
following is my code.
private function init():void
{
if (queFlag == 'a')
{
// timeLbl.text = currentTimeLbl.text using some binding mechanism
}
else if(queFlag == 'e')
{
// timeLbl.text = 'value retrived from database' ;
}
}
here currentTimeLbl shows videoDisplay playheadtime so it changes dynamically as video plays.
please help me out.
You could do it in something like the following:
<Label id="timeLbl" text="{timeLabelText}"/>
<Label id="currentTimeLbl" change="dispatchEvent('currentTimeLblChanged')"/>
[Bindable(event = "queFlagChanged")]
[Bindable(event = "currentTimeLblChanged")]
private function get timeLabelText():String
{
if(_queFlag == 'a')
{
return currentTimeLbl.text;
}
else
{
return 'value retrived from database';
}
}
public function set queFlag(value:String):void
{
_queFlag = value;
dispatchEvent(new Event("queFlagChanged"));
}
Here is a very short way of conditional binding in Flex. If you code the conditions into MXML curly-bracket-bindings they will be transformed by the MXML compiler to listeners on all objects participating in this expression.
Here is a working example:
<mx:CheckBox id="flagBox"/>
<mx:ComboBox dataProvider="{['a','e']}" id="flagBox2"/>
<mx:TextInput id="txtBox"/>
<mx:Label text="default: {txtBox.text}"/>
<mx:Label text="conditional (bool): { (flagBox.selected)? txtBox.text: 'default' }"/>
<mx:Label text="conditional (value): { (flagBox2.selectedItem == 'a')? txtBox.text: 'default' }"/>
Checking flagBox will result in label #2 displaying "default" otherwise the text from the txtBox is displayed.
Selecting "a" in flagBox2 will result in label #3 displaying "default" otherwise the text from the txtBox is displayed.
I regularly use this for reducing my lines of code in my UI-logic and it works quite well for me. A problem of this techniques is that you can't use all logic symbols in curly-braket-bindings, such as < or &&, but i usually could life with that.
I've hit a very strange issue in adobe flex and I'm not sure how to solve it. I have a popup box that is called on creationComplete before startup of my main application. The popup simply asks for an email address and then the application is enabled showing the email address in a label component.
However, when I try to access the email address from the label component called UserID.text in the application, it is always null even though it is visually present in the label box...It seems like it loses state somehow...so HOW in earth can I keep it from losing state? I need to access the label or some variable throughout the use of the application and everything I try always ends up in a null variable???
The main part of the application that sets the label is here:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:controls="com.iwobanas.controls.*" xmlns:local="*" creationComplete="showWindow(true)">
private function showWindow(modal:Boolean):void
{
var logonWindow:LogonWindow = new LogonWindow();
logonWindow.addEventListener("logon", logonHandler);
PopUpManager.addPopUp(logonWindow, this, modal);
PopUpManager.centerPopUp(logonWindow);
}
public function logonHandler(event:LogonEvent):void
{
UserID.text=event.userId;
}
Any help would be greatly appreciated.
Store the id as a Bindable variable in your application instead of setting the text input directly.
[Bindable]
private var logonId:String;
public function logonHandler(event:LogonEvent):void
{
logonId=event.userId;
}
<mx:TextInput text="{logonId}" />
What I am trying to accomplish to to get financial data in my Flex Datagrid to be color-coded--green if it's positive; red if it's negative. This would be fairly straightforward if the column I want colored was part of the dataProvider. Instead, I am calculating it based on two other columns that are part of the dataProvider. That would still be fairly straightforward because I could just calculate it again in the ItemRenderer, but another part of the calculation is based on the value of a textBox. So, what I think I need to be able to do is send the value of the textBox to the custom ItemRenderer, but since that value is stored in the main MXML Application, I don't know how to access it. Sending it as a parameter seems like the best way, but perhaps there's another.
Here is the current code for my ItemRenderer:
package {
import mx.controls.Label;
import mx.controls.listClasses.*;
public class PriceLabel extends Label {
private const POSITIVE_COLOR:uint = 0x458B00 // Green
private const NEGATIVE_COLOR:uint = 0xFF0000; // Red
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
/* Set the font color based on the item price. */
setStyle("color", (data.AvailableFunding >= 0) ? NEGATIVE_COLOR : POSITIVE_COLOR);
}
}
(data.AvailableFunding doesn't exist)
So does anyone know how I would go about accomplishing this?
You may want to look into ClassFactory from the Flex APIs:
This allows you to set a prototype Object with arbitrary types / values each of which will be passed to the item renderer. From the sample:
var productRenderer:ClassFactory = new ClassFactory(ProductRenderer);
productRenderer.properties = { showProductImage: true };
myList.itemRenderer = productRenderer;
The above code assumed that "ProductRenderer" has a public property called "showProductImage" which will be set with a value of "true."
Ah, so I knew about outerDocument but not parentDocument. I was able to just use parentDocument.*whatever I want from the main App and I can access it as long as it's public.
Example:
setStyle("color", (parentDocument.availableFunding >= 0) ? POSITIVE_COLOR : NEGATIVE_COLOR);
Sweet! :)
You can access the value of the TextBox directly, if you need to, by using the static Application.application object, which is accessible from anywhere in your application.
For example, if you wanted the renderers to be notified when the value of the TextInput control changes, you could do something like this (from within your ItemRenderer, and where myTextInput is the ID of the control defined in your main MXML class):
<mx:Script>
<![CDATA[
import mx.core.Application;
private function creationCompleteHandler(event:Event):void
{
Application.application.myTextInput.addEventListener(TextEvent.TEXT_INPUT, handleTextInput, false, 0, true);
}
private function handleTextInput(event:TextEvent):void
{
if (event.currentTarget.text == "some special value")
{
// Take some action...
}
}
]]>
</mx:Script>
With this approach, each item-renderer object will be notified when the TextInput's text property changes, and you can take appropriate action based on the value of the control at that time. Notice as well that I've set the useWeakReference argument to true in this case, to make sure the listener assignments don't interfere unintentionally with garbage collection. Hope it helps!
There's another technique, which, while it initially feels a little hacky is perhaps less cumbersome and cleaner in actual use.
It involves the little-observed fact that an event dispatch is, of course, synchronous and the event object can be treated as a value object populated by any event handler.
i.e. the ItemRenderer can do something like:
...
var questionEvt:DynamicEvent = new DynamicEvent('answerMeThis', true, true);
if (dispatchEvent(questionEvt))
{
if (questionEvent.answer == "some value")
....
With a corresponding handler somewhere up the view hierarchy above the renderer that has a listener on the event and does something like:
function handleAnswerMeThis(event:DynamicEvent):void
{
event.answer = "another value";
event.dataHelper = new DataHelperThingy();
}
etc.
It need not be a DynamicEvent - I'm just using that for lazy illustrative purposes.
I vote up for cliff.meyers' answer.
Here's another example on setting the properties of an itemRenderer from MXML by building a function that wraps a ClassFactory around the itemRenderer class and that injects the necessary properties.
The static function:
public static function createRendererWithProperties(renderer:Class,
properties:Object ):IFactory {
var factory:ClassFactory = new ClassFactory(renderer);
factory.properties = properties;
return factory;
}
A simple example that adds a Tooltip to each item in a list:
<mx:List dataProvider="{['Foo', 'Bar']}" itemRenderer="{createRendererWithProperties(Label, {toolTip: 'Hello'})}"/>
Reference:
http://cookbooks.adobe.com/post_Setting_the_properties_of_an_itemRenderer_from_MXM-5762.html
You use outerDocument property. Please see the fx:Component reference.
You could create an 'AvailableFunding' static variable in the ItemRenderer and then set it in the parent document.
public class PriceLabel extends Label {
public static var availableFunding:int;
...
...
SetStyle("color", (PriceLabel.availableFunding >= 0) ? NEGATIVE_COLOR : POSITIVE_COLOR);
}
In your parent document, set it when your text box gets updated
PriceLabel.availableFunding = textBox.text;
Obviously it'll be the same value for every ItemRenderer but it looks like that might be what you're doing anyway.
I like to override the set data function of the item renderer to change the renderer when the data provider changes as shown here
When you override the function you could cast the object to your object to make the availableFunding property available.
To access the text box you could try creating a public property and binding the property to the text box in the mxml file:
public var textVar:String;
<mx:itemRenderer>
<mx:Component>
<customrenderer textVar="{txtBox.text}" />
</mx:Component>
</mx:itemRenderer>
Nice ClassFactory Example here
See this example:
itemRenderer="{UIUtils.createRenderer(TextBox,{iconSrc:IconRepository.linechart,headerColor:0xB7D034,subHeaderColor:0xE3007F,textColor:0x75757D})}"
Let me start off by saying that I'm pretty new to flex and action script. Anyways, I'm trying to create a custom event to take a selected employee and populate a form. The event is being dispatched (the dispatchEvent(event) is returning true) but the breakpoints set inside of the handler are never being hit.
Here is some snippets of my code (Logically Arranged for better understanding):
Employee Form Custom Component
<mx:Metadata>
[Event(name="selectEmployeeEvent", type="events.EmployeeEvent")]
<mx:Metadata>
<mx:Script>
[Bindable]
public var selectedEmployee:Employee;
</mx:Script>
...Form Fields
Application
<mx:DataGrid id="grdEmployees" width="160" height="268"
itemClick="EmployeeClicked(event);">
<custom:EmployeeForm
selectEmployeeEvent="SelectEmployeeEventHandler(event)"
selectedEmployee="{selectedEmployee}" />
<mx:Script>
[Bindable]
private var selectedEmployee:Employee;
private function EmployeeClicked(event:ListEvent):void
{
var employeeData:Employee;
employeeData = event.itemRenderer.data as Employee;
var employeeEventObject:EmployeeEvent =
new EmployeeEvent( "selectEmployeeEvent", employeeData);
var test:Boolean = dispatchEvent(employeeEventObject);
//test == true in debugger
}
private function SelectEmployeeEventHandler(event:EmployeeEvent):void
{
selectedEmployee = event.Employee; //This event never fires
}
</mx:Script>
There are a few things conspiring to cause trouble here. :)
First, this declaration in your custom component:
<mx:Metadata>
[Event(name="selectEmployeeEvent", type="events.EmployeeEvent")]
<mx:Metadata>
... announces to the compiler that your custom component dispatches this particular event, but you're actually dispatching it with your containing document, rather than your custom component -- so your event listener never gets called, because there's nothing set up to call it.
Another thing is that, in list-item-selection situations like this one, the more typical behavior is to extract the object from the selectedItem property of the currentTarget of the ListEvent (i.e., your grid -- and currentTarget rather than target, because of the way event bubbling works). In your case, the selectedItem property of the grid should contain an Employee object (assuming your grid's dataProvider is an ArrayCollection of Employee objects), so you'd reference it directly this way:
private function employeeClicked(event:ListEvent):void
{
var employee:Employee = event.currentTarget.selectedItem as Employee;
dispatchEvent(new EmployeeEvent("selectEmployeeEvent"), employee);
}
Using the itemRenderer as a source of data is sort of notoriously unreliable, since item renderers are visual elements that get reused by the framework in ways you can't always predict. It's much safer, instead, to pull the item from the event directly as I've demonstrated above -- in which case you wouldn't need an "employeeData" object at all, since you'd already have the Employee object.
Perhaps, though, it might be better to suggest an approach that's more in line with the way the framework operates -- one in which no event dispatching is necessary because of the data-binding features you get out-of-the-box with the framework. For example, in your containing document:
<mx:Script>
<![CDATA[
import mx.events.ListEvent;
[Bindable]
private var selectedEmployee:Employee;
private function dgEmployees_itemClick(event:ListEvent):void
{
selectedEmployee = event.currentTarget.selectedItem as Employee;
}
]]>
</mx:Script>
<mx:DataGrid id="dgEmployees" dataProvider="{someArrayCollectionOfEmployees}" itemClick="dgEmployees_itemClick(event)" />
<custom:EmployeeForm selectedEmployee="{selectedEmployee}" />
... you'd define a selectedEmployee member, marked as Bindable, which you set in the DataGrid's itemClick handler. That way, the binding expression you've specified on the EmployeeForm will make sure the form always has a reference to the selected employee. Then, in the form itself:
<mx:Script>
<![CDATA[
[Bindable]
[Inspectable]
public var selectedEmployee:Employee;
]]>
</mx:Script>
<mx:Form>
<mx:FormItem label="Name">
<mx:TextInput text="{selectedEmployee.name}" />
</mx:FormItem>
</mx:Form>
... you simply accept the selected employee (which is marked bindable again, to ensure the form fields always have the most recent information about that selected employee).
Does this make sense? I might be oversimplifying given that I've no familiarity with the requirements of your application, but it's such a common scenario that I figured I'd throw an alternative out there just for illustration, to give you a better understanding of the conventional way of handling it.
If for some reason you do need to dispatch a custom event, though, in addition to simply setting the selected item -- e.g., if there are other components listening for that event -- then the dispatchEvent call you've specified should be fine, so long as you understand it's the containing document that's dispatching the event, so anyone wishing to be notified of it would need to attach a specific listener to that document:
yourContainingDoc.addEventListener("selectEmployeeEvent", yourOtherComponentsSelectionHandler);
Hope it helps -- feel free to post comments and I'll keep an eye out.
You're dispatching the event from the class in the lower code sample (DataGrid, I guess), but the event is defined in the EmployeeForm class, and the event handler is likewise attached to the EmployeeForm instance. To the best of my knowledge, events propagate up in the hierarchy, not down, so when you fire the event from the form's parent, it will never trigger handlers in the form itself. Either fire the event from the form or attach the event handler to the parent component.