Displaying RadioButton controls using the Repeater in Flex (redux)

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.

View MXML

<?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>

comps/MyComp.as

/**
 * 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.

17 thoughts on “Displaying RadioButton controls using the Repeater in Flex (redux)

  1. 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..

  2. 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="N\A" 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/.

  3. 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?

  4. 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

  5. You could also do something like the following (using the indexOf() instead of the search() 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 the search() method.

    Peter

  6. How do Repeater in ActionScript?

    I am using this way!

    But only runs the first record!

    private function Repeat (): void (
    
    var repeater: Repeater = new Repeater ();
    repeater.dataProvider = dataprovider_Controller.item;
    
    trace ( "repeater.currentItem.image:" + repeater.currentItem.image);
    var image: Image = new Image ();
    
    image.setStyle ( "HorizontalAlign", "left");
    image.source = repeater.currentItem.image;
    image.id = "image";
    trace ( "image.source:" + image.source);
    
    )
    

    I think I have to quit Repeat to make the loop of
    records, but how would this in ActionScript?

  7. 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>
    
  8. 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?

    radioRepeater = new Repeater();
    radioRepeater.dataProvider = arr;
    radioRepeater.childDescriptors = [radioDescriptor, txtInputDescriptor];
    radioRepeater.initializeRepeater(myTile, true);
    

    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

  9. 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.

  10. Hello friend

    Excellent article, I’m trying to do something close to this, but I’m having trouble, I wonder if I can help, here’s what I’m doing:
    I built a panel only via code and put it in a Canvas also via code and then a vbox and finally put a Canvas (which I want to repeat), I sought data from a php file, but so far I could not do repeat, here’s the code:

    import mx.core.UIComponentDescriptor;
    import mx.controls.Text;
    import mx.controls.Image;
    import mx.controls.Label;
    import mx.managers.PopUpManager;
    import mx.containers.Panel;
    import mx.core.Repeater;
    import mx.containers.Canvas;
    import mx.containers.VBox;
    import mx.controls.Label;
    public var canvas:Canvas;
    public var vbox:VBox;
    public var rep:Repeater;
    public var win:Panel;
    public var msgErr:Text;
    public var imgClose:Image;
    public var title:Label;

    public function winFiles(evt:Event):void {
    param = new Text();
    param.text = evt.currentTarget.automationName;
    recFiles.send();
    }

    { param.text}

    private function checkFiles(evt:ResultEvent):void {
    win = new Panel();
    imgClose = new Image();

    imgClose.source = “../images/close.png”;
    imgClose.scaleContent = true;
    imgClose.autoLoad = true;
    imgClose.toolTip = “FECHAR”;
    imgClose.setStyle(“right”,”5″);
    imgClose.setStyle(“top”,”5″);
    imgClose.y = 50;
    imgClose.buttonMode = true;
    if(evt.result.dados.registros == “0”) {
    msgErr = new Text();
    win.width = 380;
    win.height = 355;
    win.title = “:: Anexos (“+evt.result.dados.registros+”)”;
    win.x = (Application.application.width – win.width) / 2;
    win.y = (Application.application.height – win.height) / 2;
    win.layout = “absolute”;

    msgErr.text = “no file found”;
    msgErr.setStyle(“left”,”5″);
    msgErr.setStyle(“top”,”5″);
    msgErr.width = 180;
    msgErr.height = 50;

    win.addChild(msgErr);
    PopUpManager.addPopUp(win,panel1,true);
    } else {
    canvas = new Canvas();
    rep = new Repeater();
    vbox = new VBox();

    var canvBox:Canvas = new Canvas();
    var lblname:Label = new Label();
    var name:Label = new Label();
    var date:Label = new Label();
    var hour:Label = new Label();

    win.width = 380;
    win.height = 355;
    win.title = “:: Anexos (“+evt.result.dados.registros+”)”;
    win.x = (Application.application.width – win.width) / 2;
    win.y = (Application.application.height – win.height) / 2;
    win.layout = “absolute”;

    canvas.width = 355;
    canvas.height= 260;
    canvas.setStyle(“left”,”0″);
    canvas.setStyle(“top”,”50″);

    vbox.setStyle(“left”,”5″);
    vbox.setStyle(“top”,”0″);
    vbox.setStyle(“verticalGap”,”6″);
    vbox.width = 335;
    vbox.height = 260;

    canvas.addChild(vbox);

    canvBox.width = 320;
    canvBox.height= 60;
    canvBox.setStyle(“backgroundColor”,”#ededed”);
    canvBox.setStyle(“borderStyle”,”solid”);
    canvBox.setStyle(“cornerRadius”,”5″);

    vbox.addChild

    var repAnexos:UIComponentDescriptor = new UIComponentDescriptor(canvBox);

    rep.dateProvider = recAnexos.lastResult.anexos;
    rep.childDescriptors = [repAnexos];
    rep.initializeRepeater(vbox, true);

    vbox.addChild(rep);

    lblname.text = “Name:”;
    lblname.setStyle(“fontWeight”,”bold”);
    lblname.setStyle(“left”,”0″);
    lblname.setStyle(“top”,”5″);
    lblname.width = 100;

    name.setStyle(“left”,”110″);
    name.setStyle(“top”,”5″);
    name.width = 100;

    canvBox.addChild(lblname);
    canvBox.addChild(name);
    win.addChild(canvas);
    PopUpManager.addPopUp(win,panel1,true);
    }
    win.addChild(imgClose);
    imgClose.addEventListener(MouseEvent.CLICK, closeFiles);
    }
    private function closeFiles(evt:Event):void {
    PopUpManager.removePopUp(win);
    }

  11. , i want to do it the above mxml in as3… anyone help me.. i cannt repeat the datas in repeater while using in as3

    box_v=new VBox();
    babyactimg=new Image();
    rep=new Repeater();
    addChild(box_v);
    rep.dataProvider=cat.img;
    babyactimg.source=rep.currentItem.@src;
    babyactimg.width=30;
    babyactimg.height=30;
    addChild(babyactimg);
    box_v.addChild(rep);
    this was i done in as3 but i got the first record only…what can i do for get all the records from the repeater using as3..

  12. Hey Peter, this is the exact solution I needed for a component in my current project. I’m using the exact same method as demonstrated in your example but I’m having one problem that I cannot seem to solve. After the initial creation of the Repeater and its children, the parent and parentDocument property on the Repeater itself as well as it’s children is null. When I try and remove an item from the dataProvider the framework throws an RTE because it needs those properties to update the Repeater’s display list. Any insight into what I may be doing wrong?

    Thanks,
    Jamie

  13. Hi Peter,

    I am getting an error of “Error #1007: Instantiation attempted on a non-constructor. for the following code. the repeater is unable to repeat the custom component.

    wRepeater = new Repeater();
    var descProps:Object = {};
    descProps.type = recData;
    descProps.document = this;
    descProps.propertiesFactory = function():Object{return {infoData: wRepeater.currentItem}};
    var recordDescriptor:UIComponentDescriptor = new UIComponentDescriptor(descProps as RecordDataComp);
    acData = createRecordData(featureSet); //this returns as an array collection after an rest based service call.
    wRepeater.dataProvider = acData;
    wRepeater.childDescriptors = [recordDescriptor];
    wRepeater.initializeRepeater(vBox2, true); // At this point I am getting the above said error.
    vBox1.addChild(wRepeater);

    Here ‘RecordDataComp’ is the custom component made up with VBox consisting an two text components in another VBox.
    ‘acData’ is ArrayCollection
    ‘infoData’ is the public property in ‘RecordDataComp’ actionscript class.
    ‘recData’ is the instance of ‘RecordDataComp’
    ‘wRepeater’ is the instance of ‘Repeater’

    Any help would be appreciated.

    Thanks in advance.
    -Ven.

  14. Hi,
    I am having similar problem, when I have used data binding but the repeater does not repeat itself when the data source changes.
    My Code::

    The Chart data was static initially and then when I added the function “executeBindings(true);”, then, I saw the chage in my Charts. (when my dataSource was modifyied dynamically )
    However, when I have my debuffer on the after the data has been loaded initially and when i change the datasource (selectedSectors), I can see the change in selectedSectors but the debugger never comes to the tag

    In my output, my Chart data is displayed only for the modified datasource but for the rest, emplty charts are been seen.

    Would appreciate any help :)
    Thank you in advance

Comments are closed.