I want to set the enabled property on a button based on the return value of a function that has one or more parameters. How can I do this?
private function isUserAllowed (userName:Boolean):Boolean {
if (userName == 'Tom')
return true;
if (userName == 'Bill')
return false;
}
<mx:Button label="Create PO" id="createPOButton"
enabled="<I want to call isUserAllowed ('Bill') or isUserAllowed ('Tom') here>"
click="createPOButton_Click()" />
According to the Flex docs, as long as the property is bindable, you can simply do this (I've included the two extra buttons to demonstrate):
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
[Bindable]
private var currentUser:String = "Bill";
private function isUserAllowed(user:String):Boolean
{
if (user == "Bill")
{
return true;
}
return false;
}
]]>
</mx:Script>
<mx:VBox>
<mx:Button label="My Button" enabled="{isUserAllowed(currentUser)}" />
<mx:HBox>
<mx:Button label="Try Tom" click="{currentUser = 'Tom'}" />
<mx:Button label="Try Bill" click="{currentUser = 'Bill'}" />
</mx:HBox>
</mx:VBox>
</mx:Application>
Without currentUser marked [Bindable], though, it won't work.
Another way to go, if you wanted to bind more literally to the function (this is also expressed in the docs), would be to have the function respond to an event you dispatch when the current user changes, like so:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="init()">
<mx:Script>
<![CDATA[
private var _currentUser:String = "Bill";
public function set currentUser(value:String):void
{
if (_currentUser != value)
{
_currentUser = value;
dispatchEvent(new Event("userChanged"));
}
}
[Bindable(event="userChanged")]
private function isUserEnabled():Boolean
{
if (_currentUser == "Bill")
{
return true;
}
return false;
}
]]>
</mx:Script>
<mx:VBox>
<mx:Button label="My Button" enabled="{isUserEnabled()}" />
<mx:HBox>
<mx:Button label="Try Tom" click="{currentUser = 'Tom'}" />
<mx:Button label="Try Bill" click="{currentUser = 'Bill'}" />
</mx:HBox>
</mx:VBox>
</mx:Application>
So there are a couple of ways. IMO, the second seems somehow more proper, but there's definitely nothing wrong with the first. Good luck!
<mx:Button
enabled = "{this.myFunction(this.myVariable)}"
>
or inline:
<mx:Button
enabled = "{(function(arg:Object):Boolean{ ... })(this.myVariable)}"
>
Here's what I've done a few times in similar circumstances:
<mx:Script>
<![CDATA[
[Bindable] var _username : String;
private function isUserAllowed (userName:Boolean):Boolean {
if (userName == 'Tom')
return true;
if (userName == 'Bill')
return false;
}
]]>
</mx:Script>
<mx:Button label="Create PO"
id="createPOButton"
enabled="{isUserAllowed(_username)}"
click="createPOButton_Click()" />
This way, when the Bindable _username changes, it will fire a change notification. Since the label is listening to _username changes (even if it is simply a parameter to another function), the enabled property will be re-evaluated.
<mx:Script>
<![CDATA[
[Bindable]
private var userName : String = "Tom"; // or whatever ...
[Bindable]
private var userAllowed : Boolean;
private function isUserAllowed ():Boolean {
if (userName == 'Tom')
return false;
if (userName == 'Bill')
return false;
return false;
}
]]>
</mx:Script>
<mx:Button label="Create PO" id="createPOButton" enabled="{userAllowed}" addedToStage="isUserAllowed()"/>
Related
I've reached the point where I have got a dropdown in which is embedded a tree. Each node has got a checkbox.
The idea is to be able to navigate and tick the checkboxes without having the dropdown closing.
Can't make this dropdown to remain open after a checkbox has been clicked!
The event that closes the dropdown occurs when the xml is updated in PermissionTreeItemRendererV2.as > handleChkClick(evt) > this.itemXml.#checked = {"0" or "1"};
Any idea how to tweak the code to disable this annoying event?
sample_combobox.mxml:
<?xml version="1.0"?>
<mx:Application xmlns:local="local.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="loadXML()">
<mx:Script>
<![CDATA[
import mx.collections.XMLListCollection;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.mxml.HTTPService;
public var xmlService:HTTPService = new HTTPService();
[Bindable]
public var xmlResult:XML;
[Bindable]
public var xmlList:XMLList;
[Bindable]
public var xmlTeams:XMLListCollection;
public function loadXML():void
{
xmlService.url = "mlb.xml"
xmlService.resultFormat = "e4x";
xmlService.addEventListener(ResultEvent.RESULT, resultHandler);
xmlService.send();
}
public function resultHandler(event:ResultEvent):void
{
xmlResult = XML(event.result);
xmlList = xmlResult.league;
xmlTeams = new XMLListCollection(xmlList);
}
]]>
</mx:Script>
<local:TreeComboBox
width="300"
id="combo"
labelField="#label" dataProvider="{xmlTeams}" />
</mx:Application>
TreeCombobox.mxml:
<?xml version="1.0" encoding="utf-8"?>
<local:ComboBoxNoClose xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:local="local.*">
<mx:Script>
<![CDATA[
import mx.events.FlexEvent;
[Bindable]
private var _label:String;
[Bindable]
public var treeSelectedItem:Object;
public function updateLabel(event:*):void
{
_label = event.currentTarget.selectedItem[this.labelField];
treeSelectedItem = event.currentTarget.selectedItem;
}
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(dropdown && _label != null){
text = "";//_label;
}
}
]]>
</mx:Script>
<local:dropdownFactory>
<mx:Component>
<mx:Tree change="outerDocument.updateLabel(event)" height="500"
width="500"
itemRenderer="local.PermissionsTreeItemRendererV2"
folderClosedIcon="{null}"
folderOpenIcon="{null}"
defaultLeafIcon="{null}" />
</mx:Component>
</local:dropdownFactory>
</local:ComboBoxNoClose>
PermissionTreeItemRendererV2.as
// ActionScript file
package local
{
import flash.events.Event;
import flash.events.MouseEvent;
import mx.collections.ArrayCollection;
import mx.collections.ArrayList;
import mx.collections.ListCollectionView;
import mx.controls.CheckBox;
import mx.controls.treeClasses.TreeItemRenderer;
import mx.controls.treeClasses.TreeListData;
public class PermissionsTreeItemRendererV2 extends TreeItemRenderer{
public var chk:CheckBox;
public var itemXml:XML;
public function PermissionsTreeItemRendererV2(){
super();
mouseEnabled = false;
}
override public function set data(value:Object):void{
if(value != null){
super.data = value;
this.itemXml = XML(value);
if(this.itemXml.#checked == "1"){
this.chk.selected = true;
}else{
this.chk.selected = false;
}
}
}
override protected function createChildren():void{
super.createChildren();
chk = new CheckBox();
chk.addEventListener(MouseEvent.CLICK, handleChkClick);
addChild(chk);
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
super.updateDisplayList(unscaledWidth,unscaledHeight);
if(super.data){
var tld:TreeListData = TreeListData(super.listData);
//In some cases you only want a checkbox to appear if an
//item is a leaf
//if so, then keep the following block uncommented,
//otherwise you can comment it out to display the checkbox
//for branch nodes
if(tld.hasChildren){
this.chk.visible = true;
}else{
//You HAVE to have the else case to set visible to true
//even though you'd think the default would be visible
//it's an issue with itemrenderers...
this.chk.visible = true;
}
if(chk.visible){
//if the checkbox is visible then
//reposition the controls to make room for checkbox
this.chk.x = super.label.x
super.label.x = this.chk.x + 17;
this.chk.y = super.label.y+8;
}
}
}
private function handleChkClick(evt:MouseEvent):void
{
if(this.chk.selected)
{
this.itemXml.#checked = "1";
}
else
{
this.itemXml.#checked = "0";
}
}
}
}
ComboboxNoClose.as:
package local
{
import flash.events.Event;
import flash.events.MouseEvent;
import mx.controls.CheckBox;
import mx.controls.ComboBox;
import mx.events.DropdownEvent;
import mx.events.ListEvent;
public class ComboBoxNoClose extends ComboBox
{
public function ComboBoxNoClose()
{
super();
}
public function onOpen(event:Event):void
{
event.stopImmediatePropagation();
}
public override function close(trigger:Event = null):void
{
if (trigger != null)
{
super.close();
}
}
}
}
mlb.xml that populates the tree:
<?xml version="1.0" encoding="utf-8"?>
<root>
<league label="American League">
<division label="West">
<team label="Los Angeles" />
<team label="Seattle" />
<team label="Oakland" />
<team label="Texas" />
</division>
<division label="Central">
<team label="Cleveland" />
<team label="Detroit" />
<team label="Minnesota" />
<team label="Chicago" />
<team label="Kansas City" />
</division>
<division label="East">
<team label="Boston" />
<team label="New York" />
<team label="Toronto" />
<team label="Baltimore" />
<team label="Tampa Bay" />
</division>
</league>
</root>
Default behavior of popup is that, when you click on dropdown it closes it self. You can solve this problem like this
<mx:PopUpButton id="popup" width="100%" label="{label}" close="popup_closeHandler(event)" open="popup_openHandler(event)" openAlways="true">
<mx:popUp>
<mx:VBox width="{popup.width*1.25}" mouseEnabled="false" verticalGap="1">
<mx:List id="listSelectAll" width="100%" rowCount="1" selectable="true" itemClick="listSelectAll_itemClickHandler(event)">
<mx:dataProvider>
<mx:Array>
<mx:Object id="selectAll" selected="" label="All"/>
</mx:Array>
</mx:dataProvider>
<mx:itemRenderer>
<mx:Component>
<mx:HBox width="100%" height="100%" mouseChildren="false">
<mx:CheckBox selected="{data.selected}" label="{data.label}" width="100%"/>
</mx:HBox>
</mx:Component>
</mx:itemRenderer>
</mx:List>
<mx:List id="listItems" width="100%" dataProvider="{_dataProvider}" itemClick="listItems_itemClickHandler(event)" variableRowHeight="true">
<mx:itemRenderer>
<mx:Component>
<!--
<mx:HBox width="100%" height="100%" mouseChildren="false" verticalAlign="middle">
<mx:CheckBox selected="{data[outerDocument.selectedField]}" label="{data[outerDocument.labelField]}" width="100%"/>
</mx:HBox>
-->
<mx:HBox width="100%" mouseChildren="false" verticalAlign="middle" horizontalAlign="left" paddingLeft="4">
<mx:Script>
<![CDATA[
override public function set data(value: Object) : void {
super.data = value;
if(data.iconCache == null || outerDocument.cacheIcon == false) {
imgIcon.source = data[outerDocument.iconField];
} else {
imgIcon.source = new Bitmap(data.iconCache);
}
}
protected function image_ioErrorHandler(event:IOErrorEvent):void {
imgIcon.visible = imgIcon.includeInLayout = false;
}
protected function imgIcon_completeHandler(event:Event):void {
imgIcon.visible = imgIcon.includeInLayout = true;
if(outerDocument.cacheIcon) {
var bitmapData:BitmapData = Bitmap(imgIcon.content).bitmapData;
//var bitmap:Bitmap = new Bitmap(bitmapData);
data.iconCache = bitmapData;
//imgIcon.removeEventListener(Event.COMPLETE, imgIcon_completeHandler);
}
}
]]>
</mx:Script>
<mx:CheckBox id="chkSelected" selected="{data[outerDocument.selectedField]}"/>
<mx:Image id="imgIcon" width="{outerDocument.iconWidth}" height="{outerDocument.iconHeight}" visible="{data[outerDocument.iconField]}" includeInLayout="{data[outerDocument.iconField]}" complete="imgIcon_completeHandler(event)" ioError="image_ioErrorHandler(event)"/>
<mx:Label id="lblText" text="{data[outerDocument.labelField]}"/>
</mx:HBox>
</mx:Component>
</mx:itemRenderer>
</mx:List>
</mx:VBox>
</mx:popUp>
</mx:PopUpButton>
or you can visit my post for a complete implementation. here
I'm having a sizing issue with a canvases located inside an HBox. It seems "_graphic", "_border" and "_fill" canvases (in com.example.ThingRenderer.mxml) do not get measured at the same time as all the other measurements inside the renderer. However, this problem is only observed on the first pass-through. Refer to the images for a visual... 1st image shows the state of the app after it finished loading. 2nd image represents what the screen looks like after the Populate button is clicked. 3rd image shows what happens when the stepper is incremented. The question is how come the drawing in the 3rd image doesn't get rendered once the data is populated into the table?
RendererTest.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
creationComplete="handleCreationComplete(event)"
>
<mx:Script>
<![CDATA[
import com.example.Thing;
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import mx.events.NumericStepperEvent;
private const _thingProvider:ArrayCollection = new ArrayCollection();
private var _thing1:Thing;
protected function handleCreationComplete(event:FlexEvent):void {
_thing1 = new Thing("thingy", 0xff0000, 0.3);
_stepper.value = _thing1.ratio;
}
protected function handlePopulateClick(event:MouseEvent):void {
_thingProvider.addItem(_thing1);
}
protected function handleStepperChange(event:NumericStepperEvent):void {
_thing1.ratio = event.value;
}
]]>
</mx:Script>
<mx:VBox>
<mx:Button label="Populate" click="handlePopulateClick(event)" />
<mx:NumericStepper id="_stepper" minimum="0" maximum="1" stepSize="0.01" change="handleStepperChange(event)" />
<mx:AdvancedDataGrid dataProvider="{_thingProvider}" variableRowHeight="true" width="100%" height="100%">
<mx:columns>
<mx:AdvancedDataGridColumn headerText="Name" dataField="name" />
<mx:AdvancedDataGridColumn headerText="Display"
width="150" sortable="false"
itemRenderer="com.example.ThingRenderer"
/>
</mx:columns>
</mx:AdvancedDataGrid>
</mx:VBox>
</mx:Application>
com.exampleThingRenderer.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas
xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%"
horizontalScrollPolicy="off" verticalScrollPolicy="off"
>
<mx:Script>
<![CDATA[
import mx.binding.utils.ChangeWatcher;
private var _thing:Thing;
private var _ratioWatcher:ChangeWatcher;
private var _doClearContent:Boolean;
private var _doDrawBorder:Boolean;
private var _doUpdateFill:Boolean;
override public function set data(value:Object):void {
if(value && value is Thing) {
_thing = Thing(value);
if(_ratioWatcher) {
_ratioWatcher.unwatch();
}
_ratioWatcher = ChangeWatcher.watch(_thing, "ratio", handleRatioChanged);
_doClearContent = false;
_doDrawBorder = true;
_doUpdateFill = true;
_graphic.invalidateSize();
_border.invalidateSize();
}
else {
_doClearContent = true;
_doDrawBorder = false;
_doUpdateFill = false;
}
super.data = value;
}
private function handleRatioChanged(event:Event):void {
_doUpdateFill = true;
invalidateDisplayList();
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
if(_doClearContent) {
_container.visible = false;
_container.includeInLayout = false;
_doClearContent = false;
}
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(_doDrawBorder) {
trace("_thingContainer.width="+_container.width, "_thingGraphic.width="+_graphic.width, "_thingBorder.width="+_border.width);
_border.graphics.clear();
_border.graphics.moveTo(0, 0);
_border.graphics.lineStyle(1, _thing.color);
_border.graphics.lineTo(_border.width, 0);
_border.graphics.lineTo(_border.width, _border.height);
_border.graphics.lineTo(0, _border.height);
_border.graphics.lineTo(0, 0);
_doDrawBorder = false;
}
if(_doUpdateFill) {
_percentage.text = Math.round(_thing.ratio * 100.0) + "%";
_fill.graphics.clear();
_fill.graphics.beginFill(_thing.color);
_fill.graphics.drawRect(0, 0, _fill.width * _thing.ratio, _fill.height);
_doUpdateFill = false;
}
}
]]>
</mx:Script>
<mx:HBox id="_container" width="100%" paddingLeft="5" paddingTop="5" paddingRight="5" paddingBottom="5">
<mx:Label id="_percentage" width="45" />
<mx:Canvas id="_graphic" width="100%" height="15">
<mx:Canvas id="_border" x="0" y="0" width="100%" height="100%" />
<mx:Canvas id="_fill" x="0" y="0" width="100%" height="100%" />
</mx:Canvas>
</mx:HBox>
</mx:Canvas>
com.example.Thing.as
package com.example {
public class Thing {
[Bindable] public var name:String;
[Bindable] public var color:uint;
[Bindable] public var ratio:Number;
public function Thing(name:String, color:uint, ratio:Number) {
this.name = name;
this.color = color;
this.ratio = ratio;
}
}
}
All this happens because you can't use width and height properties in updateDisplayList, they are not updated yet. Make separate component (e.g. ThingProgressBar) and put drawing logick inside it, that will solve everything:
package
{
import mx.core.UIComponent;
public class ThingProgressBar extends UIComponent
{
private var _ratio:Number;
public function get ratio():Number
{
return _ratio;
}
public function set ratio(value:Number):void
{
_ratio = value;
invalidateDisplayList();
}
override protected function updateDisplayList(
unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
graphics.clear();
if (unscaledWidth > 0 && unscaledHeight > 0)
{
graphics.lineStyle(1, 0xFF0000);
graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
graphics.beginFill(0xFF0000);
graphics.drawRect(0, 0, unscaledWidth * ratio, unscaledHeight);
graphics.endFill();
}
}
}
}
So your renderer might look like this:
<mx:HBox
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
horizontalScrollPolicy="off" verticalScrollPolicy="off" xmlns:local="*"
>
<fx:Script>
<![CDATA[
[Bindable] private var _thing:Thing;
override public function set data(value:Object):void
{
_thing = value as Thing;
super.data = value;
}
]]>
</fx:Script>
<mx:HBox width="100%"
paddingLeft="5" paddingTop="5"
paddingRight="5" paddingBottom="5">
<mx:Label text="{_thing.name}" width="45" />
<local:ThingProgressBar width="100%" height="15"
ratio="{_thing.ratio}"/>
</mx:HBox>
</mx:HBox>
I removed watcher. Binding by watcher is considered a bad practice, use mxml binding or events instead.
I removed two Canvases with separated border and fill - they can be cobined together.
I used UIComponent instead of Canvas. Don't use containers unless you need layout, they are heavy.
I used HBox instead of Canvas in renderer because I like boxes more :) But you can't avoid using second container in renderer if you need custom styles since List overwrites renderer's stylesheet.
Hello I'm building a chat using Flex. The problem is how to do that new user get the list off all users online and added to the lists of all users. I try to put this information in DataGrid through dataProvider "callerns":
<s:DataGrid x="10" y="125" width="238" height="125" alternatingRowColors="[ #67676767, #555555]"
borderVisible="true" chromeColor="#555555" color="#CCCCCC"
contentBackgroundColor="#555555" dataProvider="{callerns}" fontWeight="bold"
requestedRowCount="4" rollOverColor="#08700D" selectionColor="#08700D"
symbolColor="#CCCCCC">
<s:columns>
<s:ArrayList>
<s:GridColumn dataField="name" headerText="USER ONLINE"></s:GridColumn>
<s:GridColumn dataField="peerID" headerText="USER ID"></s:GridColumn>
</s:ArrayList>
</s:columns>
</s:DataGrid>
Here is the part of code:
[Bindable]
private var callerns:ArrayCollection = new ArrayCollection();
...........
private function netStatusEvent(event:NetStatusEvent):void{
trace('NetConnection status event (1): ' + event.info.code);
//writeText(event.info.code);
switch(event.info.code){
case "NetConnection.Connect.Success":
log('Connected (NearID: '+nc.nearID+')', 'debug');
log('Connection sucsessful');
MyPeerID = nc.nearID;
txtFingerprint.text = MyPeerID;
initSendNetStream();
callerns.addItem({peerID: MyPeerID, name: myName});
setupGroup();
break;
case "NetGroup.Posting.Notify":
receiveMessage(event.info.message);
log('Message posted');
break;
case "NetGroup.Connect.Success":
log('Net Group connection sucsessful');
connected = true;
break;
case 'NetStream.Connect.Success':
log('Peer Connected (FarID: '+event.info.stream.farID+')', 'debug');
break;
case "NetGroup.Neighbor.Connect":
log('New user connected');
break;
}
}
private function initSendNetStream():void{
trace("initSendStream");
sendStream = new NetStream(nc,NetStream.DIRECT_CONNECTIONS);
sendStream.addEventListener(NetStatusEvent.NET_STATUS, netStatusEvent);
var clientObject:Object = new Object();
clientObject.onPeerConnect = function(callerns:NetStream):Boolean{
initRecvStream(callerns.farID);
callerns.send('onPeerNameUpdate', MyPeerID, myName);
return true;
}
sendStream.client = clientObject;
sendStream.publish('video');
log('Net Stream publish start');
}
private function initRecvStream(peerID:String):void {
//log('initRecvStream', 'debug');
var stream:NetStream = new NetStream(nc, peerID);
stream.addEventListener(NetStatusEvent.NET_STATUS, netStatusEvent);
stream.play('video');
var client:Object = new Object();
client.onPeerNameUpdate = onPeerNameUpdate;
stream.client = client;
var peer:Object = new Object();
peer.stream = stream;
recvStreams[peerID] = peer;
}
private function onPeerNameUpdate(peerID:String, name:String):void {
//log('onPeerNameUpdate received: '+peerID+':'+name, 'debug');
//log(name+' connected to your channel', 'debug');
callerns.addItem({peerID: peerID, name: name});
}
Unfortunately this doesn't work and new users aren't added to the grid. Could you please help me to solve this problem?
Removed original post seems I'm wrong, this works:
<?xml version="1.0" encoding="utf-8"?>
<s:Group creationComplete="group1_creationCompleteHandler(event)"
height="100%"
width="100%"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
[Bindable]
private var callerns : ArrayCollection;
protected function group1_creationCompleteHandler(event : FlexEvent) : void
{
callerns = new ArrayCollection();
}
protected function addItemClickHandler(event : MouseEvent) : void
{
callerns.addItem({peerID: "somePeerID", name: "someName"});
}
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout/>
</s:layout>
<s:Button click="addItemClickHandler(event)"
label="Add Item"/>
<s:Button click="propertyChangeClickHandler(event)"
label="Dispatch Property Change"/>
<s:DataGrid alternatingRowColors="[ #67676767, #555555]"
borderVisible="true"
chromeColor="#555555"
color="#CCCCCC"
contentBackgroundColor="#555555"
dataProvider="{callerns}"
fontWeight="bold"
height="125"
requestedRowCount="4"
rollOverColor="#08700D"
selectionColor="#08700D"
symbolColor="#CCCCCC"
width="238"
x="10"
y="125">
<s:columns>
<s:ArrayList>
<s:GridColumn dataField="name"
headerText="USER ONLINE">
</s:GridColumn>
<s:GridColumn dataField="peerID"
headerText="USER ID">
</s:GridColumn>
</s:ArrayList>
</s:columns>
</s:DataGrid>
</s:Group>
So I was originally wrong, hitting the button does cause an update to the grid. It would have been extremely helpful if you had posted a link to the original tutorial you were building off of, I found it and got it working with spark just fine. I'm assuming you got this part figured out already but here's the basic chat client:
<?xml version="1.0" encoding="utf-8"?>
<s:Application minHeight="600"
minWidth="955"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
private const SERVER_ADDRESS : String = "rtmfp://p2p.rtmfp.net/";
private const DEVELOPER_KEY: String = PUT_YOUR_DEV_KEY_HERE;
private var nc : NetConnection;
private var myPeerID : String;
private var farPeerID : String;
// streams
private var sendStream : NetStream;
private var recvStream : NetStream;
private function initConnection() : void
{
if (txtFingerprint.text)
{
farPeerID = txtFingerprint.text;
}
nc = new NetConnection();
nc.addEventListener(NetStatusEvent.NET_STATUS, ncStatus);
nc.connect(SERVER_ADDRESS + DEVELOPER_KEY);
}
private function ncStatus(event : NetStatusEvent) : void
{
trace(event.info.code);
myPeerID = nc.nearID;
txtFingerprint.text = myPeerID;
}
private function initSendStream() : void
{
trace("initSendStream");
sendStream = new NetStream(nc, NetStream.DIRECT_CONNECTIONS);
sendStream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
sendStream.publish("media");
var sendStreamClient : Object = new Object();
sendStreamClient.onPeerConnect = function(callerns : NetStream) : Boolean
{
farPeerID = callerns.farID;
trace("onPeerConnect " + farPeerID);
return true;
}
sendStream.client = sendStreamClient;
}
private function initRecvStream() : void
{
recvStream = new NetStream(nc, farPeerID);
recvStream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
recvStream.play("media");
recvStream.client = this;
}
public function receiveSomeData(str : String) : void
{
txtReceiveData.text = str;
}
private function sendSomeData() : void
{
sendStream.send("receiveSomeData", txtSendData.text);
}
private function netStatusHandler(event : NetStatusEvent) : void
{
trace(event.info.code);
}
]]>
</fx:Script>
<mx:TextInput id="txtFingerprint"
width="391"
x="10"
y="10"/>
<mx:Button click="initConnection()"
label="Connect"
x="409"
y="10"/>
<mx:TextInput id="txtSendData"
x="10"
y="40"/>
<mx:TextInput id="txtReceiveData"
width="251"
x="10"
y="70"/>
<mx:Button click="sendSomeData()"
label="Send data"
x="178"
y="40"/>
<mx:Button click="initSendStream()"
label="initSendStream"
x="10"
y="100"/>
<mx:Button click="initRecvStream();"
label="initReceiveStream"
x="132"
y="100"/>
<mx:Text height="122"
text="Hint: First running Flash app - click Connect to get Fingerprint PeerID. Copy and paste this PeerID to second running Flash app to the same field and click Connect. Then initSendStream and initReceiveStream on both of them and finally you can write some text and click Send data."
width="391"
x="10"
y="130"/>
</s:Application>
Get a developer key here:
http://www.adobe.com/cfusion/entitlement/index.cfm?e=cirrus
I'm not sure what's going wrong but as the commenter above says you need to include how exactly it's breaking, that is what happens when you debug (be sure to install the debug flash player for whatever browser your using, beware chrome manages it's own plugin by default) what do you see in the console, do you get any errors etc. The tutorial app works fine for me, having this deal with two way communication and maintaining a list of all connected peers seems to be the goal correct? I also don't understand the question about NetGroup, there's more on P2P that covers that here though:
http://www.adobe.com/devnet/flashmediaserver/articles/p2p_rtmfp_groups.html
I wish you would have included more information about where you started and where you're headed with all this though (and code, I always want more code :)
Here is a functional example:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
public function go():void{
Alert.show("The focus will return to txtOne. Look: ");
}
]]>
</mx:Script>
<mx:VBox>
<mx:TextInput id="txtOne" text="1" focusOut="go()"/>
<mx:TextInput id="txtTwo" text="2"/>
</mx:VBox>
</mx:Application>
When you change from txtOne to txtTwo, the Alert is showed and after pressing OK, the focus will return to txtOne. I don't want that to happen. How to solve this?
Alert.show has close callback argument, so you will know, when it is closed and set focus to something else. Update: you should have flag, indicating, that focus out event can be processed:
private var needAlert:Boolean = true;
public function go():void
{
if (needAlert)
{
Alert.show("The focus will return to txtOne. Look: ",
"", 0, null, myCloseHandler);
needAlert = false;
}
}
private function myCloseHandler(event:CloseEvent):void
{
this.setFocus();
needAlert = true;
}
Try this also to ensure the focus goes where the user wanted it too
<mx:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.managers.IFocusManagerComponent;
private var ignoreFocusChange:Boolean = false;
private var focusTargetOnReturn: IFocusManagerComponent;
public function go():void
{
if(!ignoreFocusChange){
focusTargetOnReturn = focusManager.getFocus();
Alert.show("The focus will not return to txtOne anymore. Look: ",
"", 0, null, myCloseHandler);
}
}
private function myCloseHandler(event:CloseEvent):void
{
ignoreFocusChange = true;
focusManager.setFocus(focusTargetOnReturn);
ignoreFocusChange = false;
}
]]>
</fx:Script>
<mx:VBox>
<mx:TextInput id="txtOne" text="1" focusOut="go()"/>
<mx:TextInput id="txtTwo" text="2"/>
</mx:VBox>
I have 2 checkbox repeaters: The first gets populated w/ an ArrayCollection & the second gets populated w/ the "Label" and "Data" fields selected from the first checkbox....how do I pass another item to the second checkbox? In the code below, I am currently only passing the "key" and "val" fields & would like to pass "another" field.
<?xml version="1.0" encoding="utf-8"?>
<mx: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"
creationComplete="updateOlder();"
>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import flash.events.MouseEvent;
import mx.controls.CheckBox;
import mx.core.Repeater;
[Bindable]
public var older:ArrayCollection = new ArrayCollection ([]);
[Bindable]
public var newer:ArrayCollection = new ArrayCollection ([]);
private function updateOlder():void{
older.addItem({key:'item1',val:{ name:'desc1' ,another:'another1'}});
older.addItem({key:'item2',val:{ name:'desc2' ,another:'another2'}});
older.addItem({key:'item3',val:{ name:'desc3' ,another:'another3'}});
}
private function updateNewer(evt:MouseEvent):void {
var tmpBox:CheckBox = evt.target as CheckBox;
if(tmpBox.selected) {
// add item to array
newer.addItem({key:tmpBox.label, val:tmpBox.data.val, another:tmpBox.data.another});
}
else {
// remove item from array
var newArrIndex: int = getArrayElementIndex(newer, older[tmpBox.instanceIndex]);
if(newArrIndex != -1){
newer.removeItemAt(newArrIndex);
}
}
newer.refresh();
}
private function getArrayElementIndex(arr:ArrayCollection, elementValue:Object):int{
for (var retInd: int = 0; retInd < arr.length; retInd++) {
if (arr[retInd]['key'] == elementValue['key'] && arr[retInd]['val']['name'] == elementValue['val']['name']
&& arr[retInd]['val']['another'] == elementValue['val']['another']) {
return retInd;
}
}
return -1;
}
]]>
</fx:Script>
<mx:HBox width="100%" height="100%">
<mx:VBox width="100%" height="100%">
<mx:Repeater id="oldRepeater" dataProvider="{older}">
<mx:CheckBox color="black" fontFamily="Arial" fontSize="14"
label="{oldRepeater.currentItem.key}"
data="{oldRepeater.currentItem.val}"
click="updateNewer(event);"
/>
</mx:Repeater>
</mx:VBox>
<mx:VBox width="100%" height="100%">
<mx:Repeater id="newRepeater" dataProvider="{newer}">
<mx:CheckBox color="red" fontFamily="Arial" fontSize="14"
label="{newRepeater.currentItem.key}"
data="{newRepeater.currentItem.val}"
/>
</mx:Repeater>
</mx:VBox>
</mx:HBox>
</mx:Application>
I'm not sure what exactly you want to accomplish, but it seems to me the easiest way to go is to make the Object you assign to the "val" field on each of the ArrayCollections contain both variables, i.e.
older.addItem({ key:'item1', val:{ name:'desc1' ,another:'another1'} });
and retrieve those values later:
private function getArrayElementIndex(arr:ArrayCollection, elementValue:Object):int{
for (var retInd: int = 0; retInd < arr.length; retInd++) {
if (arr[retInd]['key'] == elementValue['key'] && arr[retInd]['val']['name'] == elementValue['val']['name']
&& arr[retInd]['val']['another'] == elementValue['val']['another']) {
return retInd;
}
}
return -1;