In a previous example, “Displaying RadioButton controls using the Repeater in Flex”, we saw how you could use a Repeater in MXML to display a series of Flex RadioButton controls based on a data provider.
The following example shows how you can create a Repeater using ActionScript to accomplish the same thing.
Full code after the jump.
I’m not saying that creating a Repeater using ActionScript is the best/preferred/pretty way of doing this, but it is just one of many solutions.
<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2008/05/28/displaying-radiobutton-controls-using-the-repeater-in-flex-redux/ -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:comps="comps.*"
layout="vertical"
verticalAlign="middle"
backgroundColor="white">
<comps:MyComp />
</mx:Application>
/**
* http://blog.flexexamples.com/2008/05/28/displaying-radiobutton-controls-using-the-repeater-in-flex-redux/
*/
package comps {
import flash.events.Event;
import mx.containers.ApplicationControlBar;
import mx.containers.Canvas;
import mx.containers.Form;
import mx.containers.FormItem;
import mx.containers.HBox;
import mx.controls.Alert;
import mx.controls.Label;
import mx.controls.RadioButton;
import mx.controls.RadioButtonGroup;
import mx.core.Application;
import mx.core.Repeater;
import mx.core.UIComponentDescriptor;
import mx.styles.CSSStyleDeclaration;
import mx.styles.StyleManager;
public class MyComp extends Canvas {
private var arr:Array;
private var appControlBar:ApplicationControlBar;
private var form:Form;
private var formItem:FormItem;
private var lbl:Label;
private var hBox:HBox;
private var radioGroup:RadioButtonGroup;
private var radioRepeater:Repeater;
public function MyComp() {
super();
init();
}
private function init():void {
var alertCSS:CSSStyleDeclaration;
alertCSS = StyleManager.getStyleDeclaration("Alert");
alertCSS.setStyle("backgroundAlpha", 0.8);
alertCSS.setStyle("backgroundColor", "black");
alertCSS.setStyle("borderAlpha", 0.8);
alertCSS.setStyle("borderColor", "black");
arr = [];
arr.push({label:"Red", data:"red"});
arr.push({label:"Orange", data:"haloOrange"});
arr.push({label:"Yellow", data:"yellow"});
arr.push({label:"Green", data:"haloGreen"});
arr.push({label:"Blue", data:"haloBlue"});
radioGroup = new RadioButtonGroup();
lbl = new Label();
formItem = new FormItem();
formItem.label = "selectedValue:";
formItem.addChild(lbl);
form = new Form();
form.styleName = "plain";
form.addChild(formItem);
appControlBar = new ApplicationControlBar();
appControlBar.dock = true;
appControlBar.addChild(form);
Application.application.addChildAt(appControlBar, 0);
hBox = new HBox();
hBox.setStyle("horizontalGap", 60);
addChild(hBox);
var descriptorProps:Object = {};
descriptorProps.type = RadioButton;
descriptorProps.document = this;
descriptorProps.propertiesFactory = radioPropFac;
descriptorProps.events = {change:"radioButton_change"};
var radioDescriptor:UIComponentDescriptor = new UIComponentDescriptor(descriptorProps);
radioRepeater = new Repeater();
radioRepeater.dataProvider = arr;
radioRepeater.childDescriptors = [radioDescriptor];
radioRepeater.initializeRepeater(hBox, true);
}
private function radioPropFac():Object {
var obj:Object = {};
obj.label = radioRepeater.currentItem.label;
obj.group = radioGroup;
return obj;
}
public function radioButton_change(evt:Event):void {
var radio:RadioButton = RadioButton(evt.currentTarget);
var item:Object = radio.getRepeaterItem();
var cssObj:CSSStyleDeclaration;
cssObj = StyleManager.getStyleDeclaration("Alert");
cssObj.setStyle("modalTransparencyColor", item.data);
cssObj.setStyle("themeColor", item.data);
Alert.show(item.label, "getRepeaterItem()");
callLater(updateSelectedValue, [evt]);
}
private function updateSelectedValue(evt:Event):void {
lbl.text = radioGroup.selectedValue.toString();
}
}
}
View source is enabled in the following example.



Completely unrelated to this post, but desperately needed. How can I build an item renderer for a dataGrid that evaluates a text string in a cell and changes the background color. If it is blank/null/undefined make it red, if else it has the text N/A, NA, or N\A make it amber, etc..
Steve Walker,
Perhaps a little crude, but this should work:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" verticalAlign="middle" backgroundColor="white"> <mx:Array id="arr"> <mx:Object label="San Francisco" population="776733" year="2000" /> <mx:Object label="San Jose" population="894943" year="2000" /> <mx:Object label="San Mateo" population="92482" year="2000" /> <mx:Object label="Daly City" year="2000" /> <mx:Object label="Pacifica" population="N/A" year="2000" /> <mx:Object label="Foster City" population="NA" year="2000" /> <mx:Object label="Redwood City" population="NA" year="2000" /> <mx:Object label="Fremont" population="" year="2000" /> </mx:Array> <mx:DataGrid id="dataGrid" dataProvider="{arr}"> <mx:columns> <mx:DataGridColumn dataField="label" /> <mx:DataGridColumn dataField="year" /> <mx:DataGridColumn dataField="population" itemRenderer="comps.CustomItemRenderer" /> </mx:columns> </mx:DataGrid> </mx:Application>comps/CustomItemRenderer.as:
package comps { import mx.controls.dataGridClasses.DataGridItemRenderer; public class CustomItemRenderer extends DataGridItemRenderer { override public function validateNow():void { super.validateNow(); if (data) { switch(data.population) { case undefined: case null: data.population = ""; case "": background = true; backgroundColor = 0xFF0000; break; case "N/A": case "N\A": case "NA": background = true; backgroundColor = 0xFFFF00; break; default: background = false; backgroundColor = 0xFFFFFF; break; } } } } }Peter
For more great posts on item renderers, check out Alex Harui’s blog at http://blogs.adobe.com/aharui/, and more specifically, http://blogs.adobe.com/aharui/item_renderers/.
Thank you.
I combined the example you placed above with the one from the site you referenced and it works like a charm. I really appreciate all your assistance. I have one more problem related to this and it is how to use a wildcard in the evaluation of the string. Thanks to the ingenuity of lazy people, the data that I have has 30 or more variations of UNKNOWN (e.g. UNK, UNK101, UNK2, UNKOWN, etc) and I would like the renderer to match anything that starts with UNK. Is it possible?
Steve Walker,
Not sure if this would work with the switch statement, but it may give you a starting point:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" verticalAlign="middle" backgroundColor="white"> <mx:Array id="arr"> <mx:Object label="UNKNOWN" /> <mx:Object label="UNK" /> <mx:Object label="UnK101" /> <mx:Object label="Porridge" /> <mx:Object label="UNK2" /> <mx:Object label="Pineapples" /> </mx:Array> <mx:Script> <![CDATA[ import mx.controls.dataGridClasses.DataGridColumn; private function isUnknown(value:String):Boolean { return value.search(/UNK/i) == 0; } private function labelFunc(item:Object, col:DataGridColumn):String { if (isUnknown(item.label)) { return "yes"; } else { return "no"; } } ]]> </mx:Script> <mx:DataGrid id="dataGrid" dataProvider="{arr}"> <mx:columns> <mx:DataGridColumn dataField="label" headerText="label:" /> <mx:DataGridColumn labelFunction="labelFunc" headerText="UNKNOWN:" /> </mx:columns> </mx:DataGrid> </mx:Application>Peter
You could also do something like the following (using the
indexOf()instead of thesearch()method):private function isUnknown(value:String):Boolean { return value.indexOf("UNK") == 0; }Although the
indexOf()method would perform a case sensitive search instead of our clever case insensitive search using thesearch()method.Peter
Once again, thank you.
How do Repeater in ActionScript?
I am using this way!
But only runs the first record!
I think I have to quit Repeat to make the loop of
records, but how would this in ActionScript?
MArcio,
The example at the top of the page shows how to create a Repeater using ActionScript.
Peter
I have a complicated problem using a Repeater.
There are four new classes: Souce, SourceData, Tactic, TacticData.
Source has a name.
SourceData has a Source (thus a Source name) and quantity.
Tactic has a name.
TacticData has a Tactic (…), selected, dueDate, and done.
(Nevermind why it has to be so split up like that.)
When displayed, a Source has one or more Tactics.
If a tactic is selected, then dueDate and done are enabled.
Now the problems:
1- I’ve gotten this much to for the data supplied at compile time, but how to make the tactic’s field change at runtime?
2- Bonus: if a tactic’s done is checked, then the entire tactic is disabled (selected and dueDate) except done. And then, another tactic of the same name must appear in the same source.
I don’t know if that makes any sense the way I explain it, but here is the source and if I could just get the first problem solved, I’ll be doing alright:
Source.as:
package { public class Source { public function Source(){} public function get name():String { return _name; } public function set name(value:String):void{ _name = value; } private var _name:String = ""; } }SourceData.as:
package { public class SourceData { public function SourceData(source:Source) { _source = source; } public function name():String { return _source.name; } public function get quantity():Number { return _quantity; } public function set quantity(value:Number):void { _quantity = value; } private var _source:Source = null; private var _quantity:Number = 0; } }Tactic.as:
package { public class Tactic { public function Tactic(){} public function get name():String { return _name; } public function set name(value:String):void{ _name = value; } private var _name:String = ""; } }TacticData.as:
package { public class TacticData { public function TacticData(tactic:Tactic){ _tactic = tactic; } //this must be a get (unlike the name function of SourceData) //for the label of checkboxes //either that or jump thru some hoops when calling name to display text of function result public function get name():String { return _tactic.name; } public function get selected():Boolean { return _selected; } public function set selected(value:Boolean):void{ _selected = value; } public function get dueDate():Date { return _dueDate; } public function set dueDate(value:Date):void{ _dueDate = value; } public function get done():Boolean { return _done; } public function set done(value:Boolean):void{ _done = value; } private var _selected:Boolean = false; private var _dueDate:Date = new Date(); private var _done:Boolean = false; private var _tactic:Tactic = null; } }Main.mxml:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()"> <mx:Script> <![CDATA[ import mx.controls.CheckBox; import mx.controls.Alert; import mx.collections.ArrayCollection; [Bindable] private var sources:ArrayCollection; private function init():void{ var s1:Source = new Source(); s1.name = "Source 1" var s2:Source = new Source(); s2.name = "Source 2" var s1d:SourceData = new SourceData(s1); var s2d:SourceData = new SourceData(s2); var ta:Tactic = new Tactic(); ta.name = "tactic A"; var tb:Tactic = new Tactic(); tb.name = "tactic B"; var tc:Tactic = new Tactic(); tc.name = "tactic C"; var tad:TacticData = new TacticData(ta); var tbd:TacticData = new TacticData(tb); tbd.selected=true; var tcd:TacticData = new TacticData(tc); var a1:Array = new Array(tad,tbd); var a2:Array = new Array(tcd); sources = new ArrayCollection(); sources.addItem({name:s1d.name(),quantity:s1d.quantity,tactics:a1}); sources.addItem({name:s2d.name(),quantity:s2d.quantity,tactics:a2}); } private function checkTactic(event:Event):void{ var cb:CheckBox = event.currentTarget as CheckBox; var t:TacticData = cb.getRepeaterItem() as TacticData; } private function checkDone(event:Event):void{ var cb:CheckBox = event.currentTarget as CheckBox; var t:TacticData = cb.getRepeaterItem() as TacticData; //Alert.show(t.name); //now how to get the sourceData associated with this tacticData? } private function n():void{ var a:int = 0; a++; } ]]> </mx:Script> <mx:VBox width="100%" height="100%"> <mx:HBox horizontalGap="0" > <mx:Label text="Source" fontSize="14" fontWeight="bold" /> <mx:Label text="Quantity" fontSize="14" fontWeight="bold" /> <mx:Label text="Tactic" fontSize="14" fontWeight="bold" /> </mx:HBox> <mx:Grid horizontalScrollPolicy="auto" verticalScrollPolicy="auto" id="mainGrid" width="100%" > <mx:Repeater id="repSources" dataProvider="{this.sources}" > <mx:GridRow> <mx:GridItem colSpan="2"> <mx:HRule width="100%" /> </mx:GridItem> </mx:GridRow> <mx:GridRow> <mx:GridItem> <mx:Label id="sourceName" text="{repSources.currentItem.name}" /> </mx:GridItem> <mx:GridItem> <mx:TextInput text="{repSources.currentItem.quantity}" /> </mx:GridItem> <mx:GridItem> <mx:VBox> <mx:Repeater id="repTactics" dataProvider="{repSources.currentItem.tactics}"> <mx:HBox> <mx:CheckBox label="{repTactics.currentItem.name}" selected="{repTactics.currentItem.selected}" change="checkTactic(event)"/> <mx:DateField enabled="{repTactics.currentItem.selected}"/> <mx:CheckBox label="Done" selected="{repTactics.currentItem.done}" enabled="{repTactics.currentItem.selected}" change="checkDone(event)"/> </mx:HBox> </mx:Repeater> </mx:VBox> </mx:GridItem> </mx:GridRow> </mx:Repeater> </mx:Grid> </mx:VBox> </mx:Application>From the example above, suppose you had two items you wanted repeated and you want both of those items wrapped in an HBox. I modified the code to include a new UIComponentDescriptor for a TextInput field. How can you put the two UIComponentDescriptor’s in a HBox and tell the repeater to repeat those objects into a Tile.
Do I create another UIComponentDescriptor for the HBox or do a var myHbox:HBox = new HBox()? And then how do I put the Radio button and TextInput into myHbox and get the repeater to repeat that entire process?
All the things I’ve tried only scattered my objects all over the screen or I had one Huge long horizontal box that scrolled off the screen.
Thanks
Greg
This was a great example. I’m using the same logic but I’m using the repeater for the text component. For reasons I won’t get into, I was having problems with the line:
descriptorProps.propertiesFactory = radioPropFac;
I decided to replace the function “radioPropFac” with the something like this:
descriptorProps.propertiesFactory = function():Object {
return {
text: itemRepeater.currentItem.label
}}
…that worked like a charm.