AS
[Bindable]
var object:Object = {
property: "Property"
};
MXML
<s:Label text="{object.property}"/>
The labels text will be "Property", but if object.property is changed, the label isn't updated. Is there any way around this?
Properties of an object or collection will not dispatch a property change event unless implemented.
Likewise to your example, a change to an Array element will not be bound.
Collections such as ArrayCollection wrap objects within a proxy to dispatch events for binding.
Use an ObjectProxy to dispatch changes to your object.
Instantiate an ObjectProxy and listen for PropertyChangeEvent:
objectProxy = new ObjectProxy(object);
objectProxy.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChangeHandler);
Access your object via the proxy, such as setting a property named 'property':
objectProxy.property = "Hello, world";
This example creates an ObjectProxy with a timer to change the 'property' member every second:
<?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="creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import mx.events.PropertyChangeEvent;
import mx.utils.ObjectProxy;
private var object:Object = {};
private var objectProxy:ObjectProxy;
private var timer:Timer;
protected function creationCompleteHandler(event:FlexEvent):void
{
objectProxy = new ObjectProxy(object);
objectProxy.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChangeHandler);
timer = new Timer(1000);
timer.addEventListener(TimerEvent.TIMER, timerHandler);
timer.start();
}
protected function propertyChangeHandler(event:PropertyChangeEvent):void
{
label.text = (event.source).property;
}
protected function timerHandler(event:TimerEvent):void
{
objectProxy.property = Math.random() * 1000;
}
]]>
</fx:Script>
<s:Label id="label" />
</s:Application>
You're binding to object itself. That means, that the text of the label would change only if reference to object will be changed.
object={property:"Hello World"}
In this case the binding will do the job.
If you want the text to update after some property change, you should make this property Bindable.
public class MyObject {
[Bindable]
public var property:String;
}
Then declare the variable of class MyObject and bind to its property.
Related
I am using a component to display the popup and using an event listener to get popover properties and remove the popup in the Parent. The poup var, however, in the listeners popup var is nul so it throws an error.
Any suggestions would be greatly appreciated.
John
Here is my EditStudentLogInForm.mxml component..
<?xml version="1.0"?>
<!-- containers\layouts\myComponents\MyLoginForm.mxml -->
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="handleCreationComplete();">
<mx:Script>
<![CDATA[
import mx.managers.PopUpManager;
[Bindable] public var studentLoginEmail:String;
]]>
</mx:Script>
<mx:Form width="333">
<mx:FormItem label="Email">
<mx:TextInput id="username" width="207"/>
</mx:FormItem>
<mx:FormItem label="Password">
<mx:TextInput id="password"
width="205"/>
</mx:FormItem>
</mx:Form>
<mx:HBox>
<mx:Button id="okButton" label="OK"/>
<mx:Button id="cancelButton" label="Cancel" />
</mx:HBox>
</mx:TitleWindow>
Here is the Parent...
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:s="library://ns.adobe.com/flex/spark">
<mx:Script>
<![CDATA[
import flash.events.Event;
import mx.managers.PopUpManager;
import mx.core.IFlexDisplayObject;
import EditStudentLogInForm;
import mx.containers.TitleWindow;
public var helpWindow:EditStudentLogInForm;
public function showLogin():void {
// Create the TitleWindow container.
var helpWindow:EditStudentLogInForm = EditStudentLogInForm(
PopUpManager.createPopUp(this, EditStudentLogInForm, true));
helpWindow.username.text = "johnbdh#myserver.com";
helpWindow["cancelButton"].addEventListener("click", removeMe);
helpWindow["okButton"].addEventListener("click", submitData);
}
// OK button click event listener.
private function submitData(event:Event):void {
testText.text = helpWindow.username.text;
//*********helpWindow is nul*******
removeMe(event);
}
// Cancel button click event listener.
private function removeMe(event:Event):void {
PopUpManager.removePopUp(helpWindow);
}
]]>
</mx:Script>
</mx:Application>
When you do
public function showLogin():void {
var helpWindow:EditStudentLogInForm = ...
}
you're declaring and instantiating a new variable helpWindow inside the scope of the showLogin method. This means that the instance you assigned to this locally scoped variable can not be accessed outside the showLogin method.
You did declare another variable helpWindow on the class scope (your class being the main application in this case), but you're never assigning any instance to it (since you're assigning this popup instance to the helpWindow variable that lives only in showLogin.
Hence when you try to access this variable in another method, it's value is null.
The solution is simple enough: just assign the popup instance to the class-scoped variable:
public function showLogin():void {
helpWindow = EditStudentLogInForm(
PopUpManager.createPopUp(this, EditStudentLogInForm, true)
);
...
}
On a side note: if you have a variable of the same name on the class and inside a method, the most locally scoped one always takes precedence:
public var s:String = 'class';
public function myMethod():void {
var s:String = 'method';
trace(s); // prints method
trace(this.s); // prints class
}
public function myOtherMethod():void {
trace(s); // prints class
trace(this.s); // prints class
}
I need to enable two way data binding in combobox’s selectedItem and a valueobject’s one of the field. I am using #{variable name} construct.
It works one way - when valueobject’s field is changed, combobox’s selectedItem is getting updated.
But reverse is not working unless I handle combobox’s change event explicitly.
Is there any reason for # not working as per expectation.
In below code snippet, I am trying to bind OrderInfo.billingName to combo1.selectedItem.
1st use case : OrderInfo.billingName’s initial value is getting set to combo1.selectedItem
2nd use case: In case OrderInfo.billingName value is changed in between, then also combo1.selectedItem is getting updated
3rd use case : When user select some value from combo1, it is not getting assigned to OrderInfo.billingName unless I handle change event.
[Bindable]
public class OrderInfo {
public var billingName:String ; //This field is bindable to combo1’s selectedItem
public var billingAddress:String;
public var billingCity:String;
public var billingState:String;
public var billingZip:String;
public var cardType:String;
public var cardNumber:String;
public var cardExpirationMonth:String;
public var cardExpirationYear:String;
public var deliveryDate:Date;
public function OrderInfo() {
}
public function toString ():String {
return "I am OrderInfo : " + this.billingName + this.billingCity;
}
}
<?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"
initialize="application1_initializeHandler(event)">
<fx:Script>
<![CDATA[
import mx.binding.utils.BindingUtils;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.events.IndexChangedEvent;
import mx.utils.ObjectUtil;
import spark.events.IndexChangeEvent;
[Bindable]
public var dp:ArrayCollection = new ArrayCollection (
[ {Id:'one', Amount:1000},
{Id:'two', Amount:2000},
{Id:'three', Amount:3000}
]
);
[Bindable]
public var orderInfo:OrderInfo = new OrderInfo();
protected function application1_initializeHandler(event:FlexEvent):void
{
//Initial value of the field .. this could be coming via database
orderInfo.billingName = 'one';
}
protected function combo1_changeHandler(event:IndexChangeEvent):void
{
orderInfo.billingName = (((event.currentTarget as
ComboBox).selectedItem as Object).Id); //??
}
protected function button1_clickHandler(event:Event):void
{
mx.controls.Alert.show(ObjectUtil.toString(orderInfo));
}
protected function button2_clickHandler(event:Event):void
{
// Some backend process changed the value object
orderInfo.billingName = 'three';
}
]]>
</fx:Script>
<s:ComboBox id="combo1" x="81" y="65"
dataProvider="{dp}"
labelField="Id"
selectedItem="#{orderInfo.billingName}"
change="combo1_changeHandler(event)"
/>
<s:Button label="Get OrderInfo Object Snapshot" click="button1_clickHandler(event)"
x="273" y="176"/>
<s:Button label="Change OrderInfo Object" click="button2_clickHandler(event)"
x="52" y="176"/>
I am also looking into getting this functionality working. I believe the issue lies in what is being bound. The selectedItem property expects an object of type * which means an item from the arrayCollection that it's bound with. So try passing one or these objects
({Id:'one', Amount:1000},{Id:'two', Amount:2000},{Id:'three', Amount:3000})
to the selectedItem property instead of a string.
I have a datagrid which contains a Spark dropdownlist that needs to obtain dynamic data. The datagrid uses a separate dataProvider.
When I use a static ArrayCollection within my ItemRenderer, it works (please see listing 1).
However, when I use Swiz to mediate a 'list complete' event to load the ArrayCollection, the dropdownlist does not show the new data (please see listing 2).
Using the debugger, I inspected the dropdownlist ItemRenderer and have confirmed the new data is being loaded into the ArrayCollection. The new data is not shown in the UI control. I have tried invalidateProperties() + validateNow() and dispatching events on both the control and the renderer (this), but nothing seems to make the new data appear in the control on the datagrid.
Please help !!!
Listing 1: Datagrid and static ArrayCollection (this works):
<mx:DataGrid x="10" y="25" width="98%" id="dgInventory" paddingLeft="25" paddingRight="25" paddingTop="25" paddingBottom="25"
editable="true"
itemClick="dgInventory_itemClickHandler(event)" dataProvider="{acInventory}"
creationComplete="dgInventory_creationCompleteHandler(event)"
height="580">
<mx:columns>
<mx:DataGridColumn headerText="Item" dataField="itemName" itemRenderer="components.ItemRendererItem"
rendererIsEditor="true" editorDataField="selection" editable="true"/>
<mx:DataGridColumn headerText="Quantity Required" dataField="quantityReq" itemRenderer="components.ItemRendererQuantityRequired"
rendererIsEditor="true" editorDataField="selection" editable="true"/>
</mx:columns>
</mx:DataGrid>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import spark.events.IndexChangeEvent;
public var selection:int;
[Bindable]
protected var acItem:ArrayCollection = new ArrayCollection(
[ { itemName: "Item1"},
{ itemName: "Item2"},
{ itemName: "Item3"},
]);
//
protected function dropdownlist1_changeHandler(e:IndexChangeEvent):void
{
selection = e.newIndex;
}
]]>
</fx:Script>
<s:DropDownList id="ddlItem" prompt="Select Item" dataProvider="{acItem}" labelField="itemName"
selectedIndex="{int(dataGridListData.label)}"
change="dropdownlist1_changeHandler(event)"
width="80%" top="5" bottom="5" left="5" right="5"/>
Listing 2: Dynamic ArrayCollection (does not work):
<?xml version="1.0" encoding="utf-8"?>
<s:MXDataGridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
focusEnabled="true">
<fx:Script>
<![CDATA[
import event.ItemEvent;
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import spark.events.IndexChangeEvent;
public var selection:int;
//
[Bindable]
protected var acItem:ArrayCollection = new ArrayCollection();
//
protected function dropdownlist1_changeHandler(e:IndexChangeEvent):void
{
selection = e.newIndex;
}
//
protected function ddlItem_creationCompleteHandler(event:FlexEvent):void
{
var eve : ItemEvent = new ItemEvent( ItemEvent.LIST_ITEM_REQUESTED );
dispatchEvent( eve );
}
//
[Mediate( event="ItemEvent.LIST_ITEM_COMPLETE", properties="acItem" )]
public function refreshness( _acItem : ArrayCollection ):void
{
acItem.removeAll();
var len:int = _acItem.length;
if (len > 0)
{
for (var i:int=0; i < len; i++)
{
var newItem:Object = new Object;
newItem["itemName"] = _acItem[i].itemName;
acItem.addItem(newItem);
}
}
this.invalidateProperties();
this.validateNow();
//dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
]]>
</fx:Script>
<s:DropDownList id="ddlItem" prompt="Select Item" dataProvider="{acItem}" labelField="itemName"
selectedIndex="{int(dataGridListData.label)}"
creationComplete="ddlItem_creationCompleteHandler(event)"
change="dropdownlist1_changeHandler(event)"
width="80%" top="5" bottom="5" left="5" right="5"/>
</s:MXDataGridItemRenderer>
After re-reading Peter Ent's ItemRenderer series, this turned out to be quite simple.
I extended DataGrid to have the ArrayCollection property I needed, then added this to my renderer:
[Bindable]
protected var acItem:ArrayCollection = new ArrayCollection();
//
override public function set data( value:Object ) : void
{
super.data = value;
acItem = (listData.owner as MyDataGrid).itemList; // get the data from the renderer's container (by extending it to add a property, if necessary)
}
Hi i have a mx:List with a DataProvider. This data Provider is a ArrayCollection if FotoItems
public class FotoItem extends EventDispatcher
{
[Bindable]
public var data:Bitmap;
[Bindable]
public var id:int;
[Bindable]
public var duration:Number;
public function FotoItem(data:Bitmap, id:int, duration:Number, target:IEventDispatcher=null)
{
super(target);
this.data = data;
this.id = id;
this.duration = duration;
}
}
my itemRenderer looks like this:
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" >
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
]]>
</fx:Script>
<s:Label text="index"/>
<mx:Image source="{data.data}" maxHeight="100" maxWidth="100"/>
<s:Label text="Duration: {data.duration}ms"/>
<s:Label text="ID: {data.id}"/>
</mx:VBox>
Now when i am scrolling then all images that leave the screen disappear :(
When i take a look at the arrayCollection every item's BitmapData is null.
Why is this the case?
I changed Datatype of data in Class FotoItem from Bitmap to BitmapData
in the ItemRenderer i do the following:
override public function set data( value:Object ) : void {
super.data = value;
pic.source = new Bitmap(value.image);
}
this works now. No idea why it is not working with bitmaps
I think it might be something with your use of data.data - I believe data is a reserved keyword in Actionscript, and it might be best to name your image property something else, such as data.imageData.
I'm also not sure why you're importing ArrayCollection into your item renderer as you don't appear to be using it in your itemRenderer.
You may also be running into problems with itemRenderer recyling. You may want to override public function set data() and handle setting the individual item properties there instead of relying on binding.
Where are you looking at the arrayCollection to see that the bitmapData is null?
I am having a problem with binding values in my ActionScript components. I basically want to set the value of a a variable in my component to a value in the model, and have the component variable automatically update when the model value is updated. I think that I just don't fully understand how data binding works in Flex - this is not a problem when using MXML components, but, when using ActionScript classes, the binding does not work.
This is the code I'm using, where the values are not binding:
package
{
public class Type1Lists extends TwoLists
{
public function Type1Lists()
{
super();
super.availableEntities = super.composite.availableType1Entities;
super.selectedEntities = super.composite.selectedType1Entities;
}
}
}
package
{
public class Type2Lists extends TwoLists
{
public function Type2Lists()
{
super();
super.availableEntities = super.composite.availableType2Entities;
super.selectedEntities = super.composite.selectedType2Entities;
}
}
}
/* TwoLists.mxml */
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
public var __model:ModelLocator = ModelLocator.getInstance();
public var composite:Composite =
__model.selectedComposite;
[Bindable]
public var availableEntities:ArrayCollection;
[Bindable]
public var selectedEntities:ArrayCollection;
]]>
</mx:Script>
<mx:List id="availableEntitiesList" dataProvider="{availableEntities}" />
<mx:List id="selectedEntitiesList" dataProvider="{selectedEntities}" />
</mx:HBox>
To use binding by code you should use mx.binding.utils.*
Take a look and the BindingUtils.bindProperty and bindSetter methods.
Also, be careful with manual databinding, it could lead you to memory leaks.
To avoid them, save the ChangeWatcher returned by bindProperty and bindSetter and call watcher's unwatch method when is no more used (i.e, in the dipose or destructor method)
You need to add the [Bindable] tag either to the class itself (making all properties bindable) or the properties you want to be [Bindable]. Marking properties or objects as [Bindable] in your MXML is not sufficient.
To fix this, I simply converted the classes to MXML components, and added a private variable for my ModelLocator.
/* Type1Lists.mxml */
<?xml version="1.0" encoding="utf-8"?>
<TwoLists xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*"
availableEntities="{__model.selectedComposite.availableType1Entities}"
selectedEntities="{__model.selectedComposite.selectedType1Entities}">
<mx:Script>
<![CDATA[
import model.ModelLocator;
[Bindable]
private var __model:ModelLocator = ModelLocator.getInstance();
</mx:Script>
</TwoLists>
/* Type2Lists.mxml */
<?xml version="1.0" encoding="utf-8"?>
<TwoLists xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*"
availableEntities="{__model.selectedComposite.availableType2Entities}"
selectedEntities="{__model.selectedComposite.selectedType2Entities}">
<mx:Script>
<![CDATA[
import model.ModelLocator;
[Bindable]
private var __model:ModelLocator = ModelLocator.getInstance();
</mx:Script>
</TwoLists>