How to add dynamic objects to bindable ArrayCollection in Flex? - actionscript-3

I have an ArrayCollection that serves as dataprovider for a TabBar.
Each item added must have two properties: a label and a description.
I define these items dynamically as follows:
[Bindable] //required to serve as data provider
private var mybar:ArrayCollection;
public function init() {
mybar = new ArrayCollection();
mybar.addItem({label: "Test1", description: "long test test"});
}
<s:TabBar dataProvider="{mybar}">
<s:ItemRenderer>
<s:ButtonBarButton label="{data.label}" /> //description might also be used here, but omitted
</s:ItemRenderer>
</s:TabBar>
Problem: as the properties "label" and "description" are not set [Bindable], there is the following error log:
warning: unable to bind to property 'label' on class 'Object' (class is not an IEventDispatcher)
warning: unable to bind to property 'description' on class 'Object' (class is not an IEventDispatcher)
How could I prevent this?
The warning disappears if I use ObjectProxy:
mybar.addItem(new ObjectProxy( {label: "Test1", description: "long test test"}));
But is that correct?

The way to get rid of the warning would be using an custom class with Bindable properties.
[Bindable]
public class BarData
{
public var label:String;
public var description:String;
public function BarData(label:String, description:String)
{
this.label = label;
this.description = description;
}
}
and to do this to initialize it
public function init():void {
mybar = new ArrayCollection();
mybar.addItem(new BarData("Test1", "some description"));
mybar.addItem(new BarData("Test2", "some description"));
}
another way as described above wrap it in an ObjectProxy:
public function init():void {
mybar = new ArrayCollection();
mybar.addItem(new ObjectProxy({label:"Test1", description:"some description"}));
mybar.addItem(new ObjectProxy({label:"Test2", description:"some description"}));
}
I'm curious which SDK you're using because in my FlashBuilder, with SDK 4.7, the way you define your TabBar is leading to disappointing results. I have to do this to get it working:
<s:TabBar dataProvider="{mybar}">
<s:itemRenderer>
<fx:Component>
<s:ItemRenderer>
<s:ButtonBarButton label="{data.label}" />
</s:ItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:TabBar>
still more easy is defining it this way:
<s:TabBar dataProvider="{mybar}" />
the item renderer is not really adding extra functionality here so you could just do it this way.
Hope this helps.

Related

Tabbing does not work in mx:TileList component

I am using mx:Tilelist component to display set of textfields on screen, but when i try to traverse the fields through TAB foucs move out of the list. Please provide solution for this problem. Following is the code i am using
<mx:TileList id="tileList"
dataProvider="{collection}"
change="setCurrentIndex(tileList.selectedIndex);"
dataChange="setCurrentIndex(tileList.selectedIndex);"
columnCount="1"
columnWidth="345"
itemRenderer="components.InputParamIR"
rowHeight="30"
verticalScrollPolicy="auto"
horizontalScrollPolicy="auto"
backgroundColor="#EEEEEE"
dragEnabled="false"
dragMoveEnabled="true"
dropEnabled="true"
width="100%" height="100%"
itemClick="chartTileClick(event);"
/>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.containers.Panel;
[Bindable]
public var index:uint;
[Bindable]
public var collection:ArrayCollection = new ArrayCollection();
[Bindable]
public var isVisible:Boolean ;
public function initEventsLocal(event:Event):void
{
this.initEvents(event);
collection = new ArrayCollection();
isVisible = false;
}
private function chartTileClick(event:ListEvent):void
{
event.currentTarget.tabFocusEnabled=true;
event.currentTarget.tabEnabled=true;
}
]]>
</fx:Script>
In Flex List Itemrenderer may not get the focus since it is non editable and it will not implement the focus manager interface, you need to implement IFocusManagerComponent in your item renderer components.InputParamIR also you have to override your TileList class to enable tab for childrens.
package
{
import mx.controls.TileList;
public class MyTileList extends TileList
{
override protected function createChildren():void {
super.createChildren();
this.listContent.tabChildren = this.tabChildren
this.listContent.tabEnabled = this.tabEnabled
}
}
}
Have a look in to these
Tile list item renderer text focus solved
Issues with keyboard navigation on list with custom renderer
i hope this will help you
happy coding...

Receiving Error 1026 in AS3 and do not know why

I am getting error 1026 which is "Constructor functions must be instance methods" and I don't know why. I am creating a media player type program and am having trouble with my music portion. I made a function music player before this and copied the code over to my new project. The only thing that is different now is that the code is in a state and not the main.mxml file. This is my code below and I am receiving the error where it says "public var music:Sound;" I don't see why I am getting this error and any help would be appreciated!
<fx:Declarations>
<fx:XML id="musicXML" source="data/musak.xml" />
<s:XMLListCollection id="musicCollection" source="{musicXML.song}" />
</fx:Declarations>
<s:HGroup>
<s:DataGrid id="musicGrid" dataProvider="{musicCollection}" click="onClick()" />
<local:MusicPosition id="mProgress" width="319" height="83"/>
<s:VGroup height="55">
<s:Label text="Now playing: {musicGrid.selectedItem.#name}" />
<s:Label id="txtPosition" width="91"/>
</s:VGroup>
</s:HGroup>
<s:Button x="146" y="93" label="play" click="playMusic()"/>
<s:Button x="270" y="93" label="pause" click="pauseMusic()"/>
<fx:Script>
<![CDATA[
//set your variables
public var music:Sound;
[Bindable]public var musicChannel:SoundChannel;
public var pausedTime:Number = 0;
public function onClick():void {
if(musicChannel) {
musicChannel.stop();
//clean up the variables
music = null;
musicChannel = null;
}
music = new Sound();
music.load(new URLRequest(musicGrid.selectedItem.file.toString()));
music.addEventListener(Event.COMPLETE, onMusicLoad);
}
public function onMusicLoad(e:Event):void {
mProgress.update(0);
//new channel
musicChannel = music.play();
}
protected function onE(e:Event):void {
if(musicChannel) {
txtPosition.text = millisecondsToSeconds(musicChannel.position).toString() + "/" +
millisecondsToSeconds(music.length).toString();
mProgress.update(musicChannel.position/music.length);
mProgress.alpha = 1;
} else
mProgress.alpha = 0;
}
protected function millisecondsToSeconds(milliseconds:Number):Number {
return Math.round(milliseconds/1000);
}
public function pauseMusic():void {
if(musicChannel) {
pausedTime = musicChannel.position;
musicChannel.stop();
}
}
public function playMusic():void {
if(musicChannel) {
musicChannel = music.play(pausedTime);
}
}
]]>
</fx:Script>
Be very careful when naming thing. You cannot create a variable, or method, inside your class that is the same name as the class itself.
A common--but not required--naming convention is this:
Use CamelCase for classes. In this case, your class would be named either Music.mxml or music.as. Based on your code, it seems you already follow this convention with your MusicPosition class.
Use camelcase for method, but make the first letter lowercase. Your variable could be named music in this case. Or, you may create a variable named musicPosition. Remember in Flex, the id attribute of an MXML tag is the equivalent of a variable name.
For constants, use all uppercase. Constants are commonly used as event types.
Once, again, these are common conventions [in programming languages beyond Flex/ActionSCript] but are not required or enforced by the Flex SDK.

How can I load JSON data and use it as dataProvider for dataGroup?

Im experimenting with how to create custom item renderers in flex 4.6 and have a data source that is in JSON format... Im using the following to retrieve the json data and it works and I can access the data fine
myJSONdata[i].id
myJSONdata[i].username etc...
but am having a problem understanding how to go about making this data the data type that flex expects (ArrayList, ArrayCollection?) for the dataProvider assigned to a dataGroup.
public var loader:URLLoader = new URLLoader();
public var jsonContent:URLLoader;
public var myJSONdata:Object;
public var request:URLRequest;
public function Init():void {
request = new URLRequest("URL TO THE JSON DATA...");
loader.load(request);
loader.addEventListener(Event.COMPLETE, jsonLoaded);
}
public function jsonLoaded(event:Event):void {
jsonContent = URLLoader(event.target);
myJSONdata = JSON.parse(jsonContent.data);
trace(myJSONdata.length);
}
When I try to assign the dataProvider like this...
<s:DataGroup dataProvider="myJSONdata">
I get this error:
Initializer for 'dataProvider': values of type mx.collections.IList cannot be represented in text.
I'd like to use the same data access functionality and then have that data serve as an array that I can then use as a dataProvider for the dataGroup.
Either give your DataGroup and id and assign the dataProvider in AS3 like this :
<s:DataGroup id="myDataGroup">
//in as3
myDataGroup.dataProvider = new ArrayCollection(myJSONdata);
Or you can assign it like you do in MXML but would have to wrap the variable name in curly braces.
<s:DataGroup dataProvider="{myJSONdata}">
The reason why you are getting that error is because MXML is treating myJSONdata as a normal string.
You might still need to put the JSON array in an ArrayCollection like I did with the first example.
Hope that helps.
Though this post is old but this could still be useful, maybe to someone else.
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
<s:HTTPService id="jsonLoader" result="jsonLoader_resultHandler(event)"
url="http://example.com/sample.json" />
</fx:Declarations>
<fx:Script>
<![CDATA[
import com.adobe.serializers.json.JSONDecoder;
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
//<s:WindowedApplication .... creationComplete="initApp()">
public function initApp():void
{
//so trigger jsonLoader to load the json data from the link
// using the HTTPService immediately the application starts;
jsonLoader.send();
}
protected function jsonLoader_resultHandler(event:ResultEvent):void
{
var jsonContent:Object = (new JSONDecoder()).decode(event.result.toString());
//Assuming jsonContent.data is an Array
resultDataGrid.dataProvider = new ArrayCollection(jsonContent.data);
}
]]>
</fx:Script>

how to work with event in Flex?

import flash.events.Event;
public class RequestEvent extends Event
{
public static const REQUEST:String = "request";
private var Results:Boolean;
public function get Results():Boolean
{
return _Results;
}
public function RequestEvent(Results:Boolean=false)
{
super(REQUEST);
Results = Results;
}
override public function clone():Event
{
return new RequestEvent(Results);
}
}
}
hi can some body explain why we are doing overridding of function clone and calling super(request), new in flex ........so don't mind.
One needs to implement the clone method just so that Flex could re-clone the event in the case when an event handler wishes to dispatch the same event again. Flex does provide a default implementation but one may override the method to clone the event differently, if need be.
As for calling the super method, you must call the super becasue you are extending the Event class. The type (in your case, REQUEST) must be a unique string that would uniquely identify the event to Flex platform.
Hope it helps
Regards.
The question about the overriding of the clone method in custom events is very popular and it seems to be one of the Flex strange things. You can read about it here.
So you have to override this method and only in this method you can define values of custom properties. The method is not usual, so if you try to debug it you will never get the debugger in its body.
If you try to define the value of your custom property in the constructor, the value will be ignorred.
It can be unpractical to use a constant string as the event's type value. In this case all instances of your RequestEvent are of the same type and you could not tell them appart using in different situations. As you can see in the example below, this string is used in action listener to map the listener function.
In this example I have three buttons with different events - normal Event, my version of RequestEvent and your version of it. Have a look at it, I hope it can help to understand the case.
//Application
<fx:Script>
<![CDATA[
import fld02.com.customevent.RequestEvent;
import fld02.com.customevent.RequestEvent2;
import mx.controls.Alert;
private function onCustomGroupBtn2Clicked(evt:RequestEvent):void
{
Alert.show('Btn2Clicked: results = ' + evt.results.toString(), 'This is RequestEvent');
}
private function onCustomGroupBtn3Clicked(evt:RequestEvent2):void
{
Alert.show('Btn3Clicked: Results = ' + evt.Results.toString(), 'This is your RequestEvent');
}
]]>
</fx:Script>
<customevent:CustomGroup
BUTTON1_CLICKED="{Alert.show('Btn1Clicked', 'This is Event')}"
BUTTON2_CLICKED="onCustomGroupBtn2Clicked(event)"
request="onCustomGroupBtn3Clicked(event)"/>
</s:Application>
//CustomGroup
<?xml version="1.0" encoding="utf-8"?>
<s:Group 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="346" height="144">
<fx:Metadata>
[Event(name="BUTTON1_CLICKED", type="flash.events.Event")]
[Event(name="BUTTON2_CLICKED", type="fld02.com.customevent.RequestEvent")]
[Event(name="request", type="fld02.com.customevent.RequestEvent2")]
</fx:Metadata>
<fx:Script>
<![CDATA[
private function onBtn1Click():void
{
this.dispatchEvent(new Event("BUTTON1_CLICKED"));
}
private function onBtn2Click():void
{
var requestEvent:RequestEvent = new RequestEvent("BUTTON2_CLICKED");
requestEvent.results = true;
this.dispatchEvent(requestEvent);
}
]]>
</fx:Script>
<s:Button x="43" y="31" width="183" label="Generate Event" click="onBtn1Click()"/>
<s:Button x="43" y="62" width="183" label="Generate RequestEvent" click="onBtn2Click()"/>
<s:Button x="43" y="93" width="183" label="Generate Your RequestEvent" click="{this.dispatchEvent(new RequestEvent2(true))}"/>
</s:Group>
//My RequestEvent
package fld02.com.customevent
{
import flash.events.Event;
public class RequestEvent extends Event
{
private var _results:Boolean;
public function RequestEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
override public function clone():Event
{
var requestEvent:RequestEvent = new RequestEvent(this.type);
requestEvent.results = this.results;
return requestEvent;
}
public function get results():Boolean
{
return _results;
}
public function set results(value:Boolean):void
{
_results = value;
}
}
}
//Your RequestEvent
package fld02.com.customevent
{
import flash.events.Event;
public class RequestEvent2 extends Event
{
public static const REQUEST:String = "request";
public function RequestEvent2(Results:Boolean=false)
{
super(REQUEST);
Results = Results;
}
private var _Results:Boolean;
public function get Results():Boolean
{
return _Results;
}
override public function clone():Event
{
return new RequestEvent2(Results);
}
}
}

Flex3: Custom Item Renderer does not listen to events dispatched by parent

I have a List with a custom ItemRenderer. The ItemRenderer contains a Checkbox and a Label.
The component with the List has a 'select all' checkbox.
When the 'select all' checkbox is checked, it dispatches an event that each item should listen to in order to select its own checkbox.
The eventlistener is added on creationComplete of each item, and the event is dispatched correctly when the 'select all' checkbox is selected, but the listener in the custom ItemRenderer does not listen.
How do I make the ItemRenderer listen to an event that is dispatched in its parent??
I'll add some code examples to clarify:
------- container ----------
<mx:VBox>
<mx:Script>
<![CDATA[
public var user1 = new User(1, "Jack");
public var user2 = new User(2, "John");
public var user3 = new User(3, "Mary");
[Bindable]
public var users:ArrayCollection = new ArrayCollection([user1], [user2], [user3]);
public static const SELECT_ALL_USERS:String = "selectAllUsers";
private function selectAllChangeHandler():void
{
if (selectAll.selected)
dispatchEvent(new Event(SELECT_ALL_USERS,true));
}
]]>
</mx:Script>
<mx:CheckBox id="selectAll" change="{selectAllChangeHandler()}" />
<mx:List dataProvider="{users}" itemRenderer="myRenderer" />
</mx:VBox>
------- renderer ----------
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox creationComplete="{init()}">
<mx:Script>
<![CDATA[
private function init():void
{
addEventListener (Container.SELECT_ALL, selectAllHandler, false, 0, true);
}
private function selectAllHandler():void
{
checkbox.selected=true;
}
private function selected(id:int):Boolean
{
return id==1 || id==3;
}
]]>
</mx:Script>
<mx:CheckBox id="checkbox" selected="{selected(data.id)}" />
<mx:Label text="{data.name}" />
</mx:HBox>
Please note that the users ArrayCollection or its containing user objects cannot be changed because I need the values later on.
So when 'selectAll' is clicked, each checkbox in the list should also be checked.
Your custom ItemRenderers should not register an event listener with their parent, but with your "select all" checkbox, i.e.
theCheckbox.addEventListener(YourEvent.YOUR_EVENT, itemRendererSelectAllHandler);
Failing that, can you post your code which adds the event listener and which dispatches the event form the checkbox?
edit:
Here's your bug: In renderer's init(), you need to add an event listener not to the renderer, but to the container which dispatches the event. So make that a
container.addEventListener(Container.SELECT_ALL_USERS, selectAllHandler, false, 0, true);
Doing select all in flex is little complex but I will tell you the way we did it and it works great.
We created a List derived control called "ExList" which has a property "selectAllCB" which we bind it to existing check box which will work for select all logic. Please note, when we set selectAllCB property, we make ExList to listen to checkbox's events.
When checkbox is checked, we manually set selectedItems array to array of dataProvider and all items are selected.
Playing with itemRenderer doesnt work correctly because when you program your list again and again everytime you have to write so much of code.
I am attaching some sample code here below..
public class ExList extends List
{
public function ExTileList()
{
super();
this.allowMultipleSelection = true;
}
private var _selectAllCB:CheckBox = null;
[Bindable]
public function get selectAllCB():CheckBox
{
return _selectAllCB;
}
public function set selectAllCB(v:CheckBox):void
{
if(v==null)
return;
_selectAllCB = v;
v.addEventListener(ListEvent.ITEM_CLICK, handleAllChange,false, 0 , true);
v.addEventListener("change", handleAllChange,false, 0 , true);
}
private function handleAllChange(e:Event):void
{
if(_selectAllCB.selected)
{
this.selectedItems = this.dataProvider.toArray();
}
else
{
this.selectedItems = new Array();
}
}
}
And you can use it as...
<CheckBox id="sAll"/>
<ExList selectAllCB="{sAll}"/>
// Please note its in curly braces
This is how I achieved the solution:
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" height="150" verticalGap="0">
<mx:Script>
<![CDATA[
import mx.events.ListEvent;
import mx.controls.CheckBox;
import mx.collections.ArrayCollection;
public var listData:ArrayCollection;
private function selectAll():void
{
listChkBox.selectedItems = listData.toArray();
for each (var item:Object in listChkBox.selectedItems)
{
CheckBox(listChkBox.itemToItemRenderer(item)).selected = true;
}
}
private function resetAll():void
{
listChkBox.selectedItems = listData.toArray();
for each (var item:Object in listChkBox.selectedItems)
{
CheckBox(listChkBox.itemToItemRenderer(item)).selected = false;
}
}
]]>
</mx:Script>
<mx:List width="100%" height="100%" id="listChkBox" labelField="name" allowMultipleSelection="true"
dataProvider="{listData}" selectionColor="#FFFFFF" >
<mx:itemRenderer>
<mx:Component>
<mx:CheckBox >
</mx:CheckBox>
</mx:Component>
</mx:itemRenderer>
</mx:List>
<mx:HBox width="100%" backgroundColor="#E2DEDE" paddingBottom="2" paddingLeft="2" paddingTop="2" paddingRight="2" borderStyle="solid">
<mx:Button label="All" id="btnAll" click="selectAll()" />
<mx:Button label="None" click="resetAll()"/>
</mx:HBox>
</mx:VBox>
The simple solution is to use typed objects in the list of data you are presenting and then take advantage of databinding and the binding utils to capture changes to the underlying data property on the item renderer. Override the 'set data' function in the item renderer to do whatever you need when some property in the object gets changed to reflect the 'select all/de select all' state.