I have a code in MXML and ActionScript that I found in a Flex manual. The problem is with "val" variable that should be passed to the updateMyString() function calling statement as a parameter but it doesn't happen in the code. Why is that?
<?xml version="1.0"?>
<!-- binding/BindSetterAS.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.binding.utils.*;
import mx.events.FlexEvent;
// Method called when myTI.text changes.
public function updateMyString(val:String):void {
myTA.text = val.toUpperCase();
}
<!-- Event listener to configure binding. -->
public function mySetterBinding(event:FlexEvent):void {
var watcherSetter:ChangeWatcher =
BindingUtils.bindSetter(updateMyString, myTI, "text");
}
]]>
</fx:Script>
<s:Label text="Bind Setter using setter method"/>
<s:TextInput id="myTI"
text="Hello Setter" />
<s:TextArea id="myTA"
initialize="mySetterBinding(event);"/>
</s:Application>
The documentation says that BindingUtils.bindSetter expects a setter function with one argument:
setter:Function — Setter method to invoke with an argument of the current value of chain when that value changes
In this case the val parameter will be set to the current value of chain. You can read about it here: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/binding/utils/BindingUtils.html#bindSetter
Hope this helps!
var watcherSetter:ChangeWatcher = BindingUtils.bindSetter(updateMyString, myTI, "text");
the function is like this:
bindSetter(functionToCall, objectToBind, PropertyToBind)
So, you are actually binding myTI.text. This is passed as an argument to functionToCall, i.e. updateMyString. Got it?
Related
I have a CustomDataGrid that extends from DataGrid and CustomDataGridColumn that extends from DataGridColumn.
CustomDataGridColumn has member variables of type Function.
Inside my view, I inject a presentation model using parsley.
The code is as follows:
<fx:Declarations>
<spicefactory:Configure/>
</fx:Declarations>
<fx:Script>
[Inject(id="associatedDocumentsPM")]
[Bindable]
public var model:AssociatedDocumentsPM;
</fx:Script>
<customDataGrid:CustomDataGrid id="AssocDocGrid"
width="100%" height="{(documentDataList.length+2)*20}"
doubleClickEnabled="true" enabled="{modeHandler.appEnable}"
dataP="{documentDataList}"
sortableColumns="false">
<customDataGrid:columnList>
<customDataGrid:CustomDataGridColumn
textAlign="left"
dataFieldIdentifier="documentName"
headerText="Document Type"
modifyLabelField="{model.modifyLabelField}"
dataField="documentName"
isNaNZero="true"
showDataTips="true"
editable="false"/>
...more columns here...
</customDataGrid:columnList>
</customDataGrid:CustomDataGrid>
The AssociatedDocumentsPM has functions defined and these are set in the columns.
One example being for attribute modifyLabelField="{model.modifyLabelField}"
CustomDataGridColumn.myLabelField is of type Function. myLabelField inside AssociatedDocumentsPM is a public function.
The Parsley Context file is in the parent of the above file and declares the PM as follows:
AssocDocPMFactory is a class with a sole function decorated with [Factory].
So the problem is the following:
When I debug the application and check the columnList of the DataGrid, the variable modifyLabelField is null.
Are function bindings treated differently than variables? I'm using Flex 4.5.1 together with Parsley 2.4.1
I understand that injection could happen after creationComplete is invoked but I thought the binding would take care of that.
I have a feeling that the model - the PM - is null until much much later and the function binding is not triggered.
I tried to use FastInject as well but to no avail.
Is this a problem with function pointers and Flex binding?
No it isn't. If you have these kind of doubts, it's always a good idea to quickly set up a simple isolated test situation that verifies your assumption. I created the following to test yours:
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
creationComplete="onCreationComplete()" >
<fx:Script>
<![CDATA[
private function onCreationComplete():void {
test = function(item:*):String {
return "AAA";
}
}
[Bindable]
private var test:Function;
]]>
</fx:Script>
<s:List labelFunction="{test}">
<s:dataProvider>
<s:ArrayList>
<fx:String>A</fx:String>
<fx:String>B</fx:String>
<fx:String>C</fx:String>
</s:ArrayList>
</s:dataProvider>
</s:List>
</s:Application>
If the test Function variable is declared Bindable, you'll see 3 times "AAA". If you remove the Bindable metadata, you'll see "A", "B", "C".
So clearly binding works with function pointers too (and you'll have to look elsewhere to find your nullpointer).
I have two scripts in a View, one script is inside a Component.
In the Component script I need to add numbers from data as they are added to a list and then display the total in a label placed in the View.
If I declare the variable in the first script, the Component script can't see it and if I declare it in the Component Script the label can't see it.
How will I declare it so that everyone in the view can see it?
Thanks,
Kim
Here is the code, the problem is where should I place the var MyTotal so it can be used anywhere in the view:
<s:view
<fx:Script>
<![CDATA[
//if I place it here the next CDATA inside IconItemRender can't see it.
private static var MyTotal:Number=0;
]]>
</fx:Script>
<fx:Declarations>
<s:CurrencyFormatter id="usdFormatter" useCurrencySymbol="true"/>
</fx:Declarations>
<s:itemRenderer>
<fx:Component>
<s:IconItemRender ..............>
<fx:Script>
<![CDATA[
//if I place it here the Label "TotalAmountLb" can't see it.
// and it get reset to 0 everytime I call the function getInvoiceAmount.
private static var MyTotal:Number=0;
private function getInvoiceAmount(item:Object):String
{
MyTotal = MyTotal + Number(item.Amount);
}
]]>
</fx:Script>
</s:IconItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:List>
<s:Label id="TotalAmountLb" text="{usdFormatter.format(MyTotal)}"/>
</s:view>
Just try this: Declare MyTotal in first script. And in component script just try to access with outerDocument.MyTotal
Found the solution, I had to add [Bindable] before the declaration.
I have a List with TextInput as item renderer. I want to get the value entered in the TextInput (form the TextInputItemRenderer) and pass it the main application to do some checks(upon tapping enter on the textInput -- see code below).
I know that we can do it thru dispatching event but I still don't understand how to pass a variable from the ItemRenderer to the main app.
Help Pls.
Thanks
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
autoDrawBackground="true" xmlns:components="components.*" width="100%"
>
<s:layout>
<s:HorizontalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
override public function set data( value:Object ) : void {
super.data = value;
}
protected function myTextInput_enterHandler(event:FlexEvent):void
{
trace(myTextInput.text);
What Next??
}
]]>
</fx:Script>
<components:ClearableTextInput text="{data.label}" id="myTextInput" enter="myTextInput_enterHandler(event)"/>
</s:ItemRenderer>
i'm not sure if I got your question correctly but would this help?
http://www.ajibanda.blogspot.com/2011/02/changing-currentstate-of-main-and.html
Instead of trying to access from MainApp to itemRenderer, i think you can do backward. Follow one of two solutions below:
In itemRenderer, assign value you want to check later to a public global variable on the MainApp. The limitation is you then only enable to check it on MainApp, not any where esle (other itemRenderer, component, module etc.)
Use EvenBus to put the value to a global container. Create a static eventBus instance in AppUtils, for example. In itemRenderer, AppUtils.eventBus.dispatch() an event with the value attached to it each time the value changed. And then use AppUtils.eventBus again to addEventListener() to retrieve the value and check wherever you want. Google AS3Commons for EventBus.
I am trying to set a variable in a titlewindow pupup and use it in a script section. The variable is not being set and I don't know why.
Here is my Main.mxml file:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" creationComplete="application1_creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import mx.managers.PopUpManager;
protected function application1_creationCompleteHandler(event:FlexEvent):void
{
var test:TestWindow = TestWindow(PopUpManager.createPopUp(this,TestWindow,true));
PopUpManager.centerPopUp(test);
test.testText = 'test 2';
test.bol = true;
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
</s:Application>
Here is my titleWindow file:
<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="400" height="300"
close="PopUpManager.removePopUp(this);" creationComplete="titlewindow1_creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import mx.managers.PopUpManager;
[Bindable] public var testText:String = 'test 1';
public var bol:Boolean = false;
protected function titlewindow1_creationCompleteHandler(event:FlexEvent):void
{
if(bol){
trace('Boolean is true');
}
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Label text='{testText}'/>
</s:TitleWindow>
The problem is the label does change from 'test 1' to 'test 2' but the bol variable will not be set, why is that?
The creationComplete event fires when your TitleWindow has been completely created and only fires once (see component lifecycle).
In your case this happens in your Main.mxml file when you add the pop up with PopUpManager.centerPopUp(test);. bol is getting updated, you're just not seeing that from a trace statement because the creationComplete event has already fired.
If you want to to change the value for bol with your current setup you'll need to do that prior to opening the pop up.
In general I feel that a better way of doing this would be by using getters and setters and data binding (or Flex's invalidation methods which is described in that first link).
Note: these links provided are for Flex 3 but so far as I know all this holds true in Flex 4.
NoobsArePeople2 is 100% correct about what's going on with the lifecycle. A quick and dirty workaround for this is to use PopUpManager.addPopUp() instead of createPopUp():
var test:TestWindow = new TestWindow();
test.testText = 'test 2';
test.bol = true;
PopUpManager.addPopUp(test, this, true);
PopUpManager.centerPopUp(test);
This should allow you to set the values on TestWindow before it actually gets initialized.
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/managers/PopUpManager.html
I am using an component, and currently have a dataProvider working that is an ArrayCollection (have a separate question about how to make this an XML file... but I digress).
Variable declaration looks like this:
[Bindable]
private var _dpImageList : ArrayCollection = new ArrayCollection([
{"location" : "path/to/image1.jpg"},
{"location" : "path/to/image2.jpg"},
{"location" : "path/to/image3.jpg"}
]);
I then refer to like this:
<s:List
id="lstImages"
width="100%"
dataProvider="{_dpImageList}"
itemRenderer="path.to.render.ImageRenderer"
skinClass="path.to.skins.ListSkin"
>
<s:layout>
<s:HorizontalLayout gap="2" />
</s:layout>
</s:List>
Currently, it would appear that each item is processed asynchronously.
However, I want them to be processed synchronously.
Reason: I am displaying a list of images, and I want the leftmost one rendered first, followed by the one to its right, and so on.
Edit:
I just found this answer.
Do you think that could be the same issue?
Instead of declaring the variable and using it as the binding source, declare two collections. Then, onCreationComplete call loadNext() which shifts an object out of the second array and pushes it into the first. When the item has been rendered (custom event dispatched by itemRenderer and caught) call loadNext() again until such time as your source array is empty and your bound dataProvider has all the images.
I can write it in code if this doesn't make any sense. ;)
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" minWidth="1024" minHeight="768" creationComplete="init()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var _source : ArrayCollection = new ArrayCollection([
{"location" : "path/to/image1.jpg"},
{"location" : "path/to/image2.jpg"},
{"location" : "path/to/image3.jpg"}
]);
[Bindable] private var dataProvider:ArrayCollection;
protected function init():void
{
this.lstImages.addEventListener( "imageLoaded", handleImageLoaded);
loadImage()
}
protected function loadImage():void
{
if(this._source.length<=0)
return;
var image:Object = this._source.getItemAt(0);
dataProvider.addItem(image);
this._source.removeItemAt(0);
}
protected function handleImageLoaded(event:Event):void
{
loadImage()
}
]]>
</fx:Script>
<s:List
id="lstImages"
width="100%"
dataProvider="{_dpImageList}"
itemRenderer="path.to.render.ImageRenderer"
skinClass="path.to.skins.ListSkin"
>
<s:layout>
<s:HorizontalLayout gap="2" />
</s:layout>
</s:List>
</s:Application>
Your item renderer's image's complete handle will dispatch:
protected function handleImageLoaded(event:Event):void
{
owner.dispatch(new Event("imageLoaded"));
}
And that should load your images in a clean sequence.