Solved

What is the best method for maintaining radio button states in Flex?

Posted on 2009-04-09
39
414 Views
Last Modified: 2013-11-11
I'm developing an application that displays a series of questions with radio buttons for answers.  The user can hit next/prev to cycle through the available questions.  This works fine.  The user submits their answer, and the answer is stored elsewhere in another data structure.  However, if the user goes back to the previous question, the "previous" answer radio button is no longer selected.  Is there a best way to handle this problem?  The radio buttons are dynamic and vary depending on how many questions an answer has.
0
Comment
Question by:csciguy81
  • 19
  • 19
39 Comments
 
LVL 12

Expert Comment

by:lexxwern
ID: 24112982
>> The user submits their answer, and the answer is stored elsewhere in another data structure.  
>> However, if the user goes back to the previous question, the "previous" answer radio button
>> is no longer selected.  Is there a best way to handle this problem?  

Make the data structure [Bindable] with the RadioButton control.

And I don't understand why selections made are getting lost. What is the creationPolicy setting on the parent container? Maybe that is having and effect?

If everything fails, you can just write a refresh() that reflects the values in the data structure onto the RadioButtons and call this method each time next and previous are called.
0
 
LVL 37

Expert Comment

by:zzynx
ID: 24113910
If your radio buttons display **at every moment** (=are bound to) the state of your data model that is behind them, going back and foward should work just fine.

Can you show us your code?
0
 

Author Comment

by:csciguy81
ID: 24119050
The data model itself consists of two components.  One is an answer component, which is what the radio buttons are based off of.  Each answer component has an answer letter, and answer text.  The answer has no idea that it is the "correct" answer.  It's just it's on single entity.

The question object contains various properties, the users previous answer, and then a collection of answer objects, per question.

So, when the user submits an answer, this answer value is stored in the particular instance of the question object.  I have the users answer available at all times.  I'm attempting to do the following, and while the match is made, the radio button itself never returns to a selected state.

answerGroup is a radio button group.
questionController.GetQuestion().userAnswerLetter returns back the current answer for that question.
//inside some function
 
   for(var i = 0; i<answerGroup.numRadioButtons; i++){
       var rb:RadioButton = answerGroup.getRadioButtonAt(i);
       
       var userAnswer:String =    questionController.GetQuestion().userAnswerLetter;
			
	if(rb.value == userAnswer){
									answerGroup.getRadioButtonAt(i).selected = true;
			break;	
	}
   }//end for
}

Open in new window

0
Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

 
LVL 37

Expert Comment

by:zzynx
ID: 24122410
That looks OK at first sight. (although it is no binding as I meant)
You say that's inside "some function", but the good working depends on when/where that function is called of course.
And apparently that isn't correct at the moment.

Could you explain when exactly you perform this code?
0
 

Author Comment

by:csciguy81
ID: 24131324
This code is performed after the previous button is pressed, and after the page itself populates.  All of the radiobuttons are already drawn when this method occurs.  I thought it might be a timing thing, but I'm seeing nothing so far.
0
 
LVL 37

Expert Comment

by:zzynx
ID: 24135496
>> However, if the user goes back to the previous question, the "previous" answer radio button is no longer selected.
Which one is selected then? The first one or none?

>> This code is performed after the previous button is pressed, and after the page itself populates.
Are you 100% sure (show me how come you are sure) that you're looking at the *right* answerGroup and the *right* questionController?
0
 

Author Comment

by:csciguy81
ID: 24137765
None are selected when I go back.

Here's the code to ensure the right group is being passed in.  The group is repopulated when the page reloads.  There is only a single button group (answerGroup) on the page itself.

Is there a better way to get this code to format propertly when you insert it on here?
public function CheckRadioButtonsAndReassign(answerGroup:RadioButtonGroup){
				if(questionController.GetQuestion().userAnswerLetter!=null){
					
     for(var i = 0; i<answerGroup.numRadioButtons; i++){
	var rb:RadioButton = answerGroup.getRadioButtonAt(i);
	var userAnswer:String = questionController.GetQuestion().userAnswerLetter;						
        if(rb.value == userAnswer){  				 answerGroup.getRadioButtonAt(i).selected = true;
	break;
							
	}
}
}
}

Open in new window

0
 

Author Comment

by:csciguy81
ID: 24140132
Slight update here.

Let's say I have a question with 4 answers.  If I press next, my radioButton group now has 8 items in it (4 for the last question, 4 for this question).  Pressing previous now tells me that I have 12! items in my radioButton group.

Each time I reload the window, the dataprovider on my repeater object is being wiped out.

Why is everything being added every time now, despite clearing the array collection that is tied to the repeater?


<mx:Tile direction="horizontal" x="10" y="292">
	
	<mx:RadioButtonGroup id="answerGroup" />
        <mx:Repeater id="rep" dataProvider="{cbCol}">
        <mx:RadioButton group="{answerGroup}" id="radios" label="{rep.currentItem.answerText}" value="{rep.currentItem.answerLetter}"  x="20" y="292"/>
	
        </mx:Repeater>
	</mx:Tile>
 
and in code, something like this...
 
cbCol = new ArrayCollection();
for(var i:int = 0; i<answersForThisQuestion.length; i++){
					cbCol.addItem(answersForThisQuestion.getItemAt(i) as Answer);
					
}
 
Does this not wipe out the datasource each time for this object?

Open in new window

0
 
LVL 37

Assisted Solution

by:zzynx
zzynx earned 250 total points
ID: 24147499
>> Why is everything being added every time now,
Aha, all you radio buttons belong to the same RadioButtonGroup.
That's certainly somehting to change.
>> despite clearing the array collection that is tied to the repeater?
It's not because you change the repeater's dataprovider that all the previously created RadioButtons are gone too.
They are still "bound" to the RadioButtonGroup.

You'll have to create a RadioButtonGroup per question.
0
 

Author Comment

by:csciguy81
ID: 24147523
So for every question that loads, I need to have a new radio button group then?  I can't just keep reassigning my old button group, since I'm rebuilding everything when they are pressing the previous button?

How would I create dynamic radiobuttongroups in that case then?
0
 
LVL 37

Expert Comment

by:zzynx
ID: 24147565
>> since I'm rebuilding everything when they are pressing the previous button?
I don't say you have to. Maybe you can just keep what you have.
Can't you use a ViewStack with one panel (or canvas or whatever) per question?
Pressing the Next/Previous button is then just selecting another panel in the viewstack.
0
 

Author Comment

by:csciguy81
ID: 24147600
Hmm.  I'm not familiar with a viewstack, but I can look in to it.  I'm trying to keep the app as small as possible.  Would going that route increase my size a lot?  I may have up to 100 questions that I need to quiz the user on.
0
 
LVL 37

Expert Comment

by:zzynx
ID: 24147701
>> I may have up to 100 questions that I need to quiz the user on.
I see, you don't want to create 100 different panels in that case.
Moreover, the panels almost have the same content.
So, yes you could build up the panel on every next/previous button press.
But then really completely recreate the panel (<> reuse the same RadioButtonGroup)
0
 

Author Comment

by:csciguy81
ID: 24147754
What about this?  What if I include in each question object a property of the radio button group itself.  If the RBG is null, then set it, if not, use the RBG that was previously created.  

Would that maintain the correct RBG, per question?
0
 
LVL 37

Expert Comment

by:zzynx
ID: 24147939
That sounds OK to me.
But I wonder if that is really needed.

I see two options:
1) You create 100 different, co-existing panels, each with their own RadioButtonGroup
2) You create a completely new panel whenever you have to present a question (on each moment only one panel & RadioButtonGroup exist)
    Pressing a button = deleting the panel and create a new one.

Since the 1st option is needless overkill, the 2nd one is the right one. But in that case why would you preserve that RadioButtonGroup?
If you recreate the RadioButtons, why wouldn't you recreate the RadioButtonGroup too?
0
 

Author Comment

by:csciguy81
ID: 24147963
See, currently it's setup so that it's just one panel.  When the user presses next, it grabs the next question object and repopulates the panel with the appropriate question text and the radio buttons.  This works fine as long as you're progressing forward.  I assumed that by changing the dataprovider each time to delete and repopulate the radio buttons, it would maintain the same internal RGB objects and whatnot, but I guess that isn't the case internally.  
0
 
LVL 37

Expert Comment

by:zzynx
ID: 24148104
>> This works fine as long as you're progressing forward.
But internally it isn't fine at all, since your previous radiobuttons are still part of the (same) RadioButtonGroup you use for the current radiobuttons.
And that means that selecting a radiobutton in the current question = deselecting the radiobutton you selected in the previous question.

>> it grabs the next question object and repopulates the panel
You'd better create a completely new one
0
 

Author Comment

by:csciguy81
ID: 24152712
How would I go about creating an entire new panel each time?

I've got a custom panel that I built here that displays these questions each time.  When the user presses the next button, it just repopulates the panel with the "current" question and answers.  Would I need to destroy the last panel and make a new instance of it each time?
0
 
LVL 37

Expert Comment

by:zzynx
ID: 24155159
>> Would I need to destroy the last panel and make a new instance of it each time?
Yes.
But it would be much easier to help if you showed me your complete code
0
 

Author Comment

by:csciguy81
ID: 24159601
ok, here is the complete code for the panel itself.

This panel lives inside of an "outer" panel that contains a previous/next button.

When the previous/next button is pressed, some logic is done in the question controller to adjust your index and serve up the next question.  The code that populates this inner window is also attached.  It is nothing more than grabbing the question and populating the text/radio buttons.


<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:Controllers="Controllers.*" layout="absolute" width="740" height="530">
	<Controllers:QuestionController id = "questionController"/>
	
	<mx:TextArea id="questionTextArea" x="10" y="10" width="700" height="274" fontSize="12" editable="false" enabled="true" wordWrap="true" creationComplete="Init();"/>
	<mx:Tile direction="horizontal" x="10" y="292">
	
	<mx:RadioButtonGroup id="myRadioGroup" />
	
        <mx:Repeater id="rep" dataProvider="{cbCol}">
        
        <mx:RadioButton id="radios" groupName="{myRadioGroup}" group="{myRadioGroup}"
        	label="{rep.currentItem.answerText}" 
        	value="{rep.currentItem.answerLetter}"  
        	x="20" y="292"/>
	
        </mx:Repeater>
	</mx:Tile>
	<mx:Script>
		<![CDATA[
			import mx.events.ItemClickEvent;
			import mx.collections.ArrayCollection;
			import mx.controls.RadioButtonGroup;
			import mx.controls.RadioButton;
			import mx.collections.XMLListCollection;
			import mx.controls.Menu;
			import Custom_Events.QuestionEvent;
			import Model.Answer;
			
			
			[Bindable]
			private var cbCol:ArrayCollection = new ArrayCollection();
			
			public function Init():void{
				this.addEventListener(QuestionEvent.QUESTION_RBCREATED,handlePreviousAnswer);
				
			}
			
			public function CreateRadioButtons(answersForThisQuestion:ArrayCollection):void{
				cbCol = new ArrayCollection();
				
				for(var i:int = 0; i<answersForThisQuestion.length; i++){
					cbCol.addItem(answersForThisQuestion.getItemAt(i) as Answer);
					
				}
 
				var questionEventObj:QuestionEvent = new QuestionEvent("radioButtonsCreated");
						questionEventObj.isEnabled=true;
						dispatchEvent(questionEventObj);
				
			}
			
			private function handlePreviousAnswer(event:QuestionEvent):void{
				CheckRadioButtonsAndReassign(myRadioGroup);
			}  
			public function CheckRadioButtonsAndReassign(answerGroup:RadioButtonGroup):void{
				if(questionController.GetQuestion().userAnswerLetter != null){
					
					
					for(var b:int = 0; b<myRadioGroup.numRadioButtons; b++){
						var rb:RadioButton = myRadioGroup.getRadioButtonAt(b);
						var userAnswer:String = questionController.GetQuestion().userAnswerLetter;
						
						if(rb.value == userAnswer){
							myRadioGroup.getRadioButtonAt(b).selected = true;
							break;	
						}
					}
				}
			}
	
		]]>
	</mx:Script>
	<mx:Image x="10" y="34" width="700" height="250"/>
	
</mx:Panel>
 
 
 
inside of the outer panel that houses the above panel.
 
//gets the current question after index adjustment.
localQuestion = questionController.GetQuestion(); 
						
if(localQuestion != null){
		
//change the text of the inner panel
					questionCheckBoxWindow.questionTextArea.text = localQuestion.questionText;
 
//create the new radio buttons
								questionCheckBoxWindow.CreateRadioButtons(localQuestion.allAnswers);
														
}

Open in new window

0
 
LVL 37

Expert Comment

by:zzynx
ID: 24167389
Your problem will be solved if you recreate your questionCheckBoxWindow for every next/previous button click:

questionCheckBoxWindow = new QuestionCheckBoxWindow();
0
 

Author Comment

by:csciguy81
ID: 24167651
See, I tried doing that, and I'm getting a null pointer on one of the internal components inside that window everytime.

If I create a new instance of the object like that, shouldn't it recreate all internal labels, boxes, etc etc that are specified in the original mxml each time?
0
 
LVL 37

Expert Comment

by:zzynx
ID: 24167768
>> I'm getting a null pointer on one of the internal components inside that window everytime.
Code and stacktrace?

>> shouldn't it recreate all internal labels, boxes, etc etc that are specified in the original mxml each time?
That's what I would expect, yes.
0
 

Author Comment

by:csciguy81
ID: 24169384
Okay, immediately when the user presses the next/previous button, I either decrement or increment the count, make a new instance of the window itself, and try to proceed on, making a new instance of the window, like so.
questionCheckBoxWindow = new QuestionPlainTextWithCheckBoxes();

I get a null pointer in either direction, with the following.

TypeError: Error #1009: Cannot access a property or method of a null object reference.
      at Panels::QuestionViewerPanel/GetQuestionAndDisplay()[\Test\src\Panels\QuestionViewerPanel.mxml:39]

This line is
questionCheckBoxWindow.questionTextArea.text = localQuestion.questionText;

localQuestion.questionText is not null.  I can see the information in the debugger.  questionCheckBoxWindow appears to have information in it in the debugger as well, and is not null.

When creating a new instance of the panel, is there anything else I need to do?  In the MXML, the panel is originally created as follows.

<QuestionDisplayTypes:QuestionPlainTextWithCheckBoxes width="100%" id = "questionCheckBoxWindow" y="-21" questionController="{questionController}">
            
      </QuestionDisplayTypes:QuestionPlainTextWithCheckBoxes>

0
 
LVL 37

Expert Comment

by:zzynx
ID: 24182618
>> This line is
>> questionCheckBoxWindow.questionTextArea.text = localQuestion.questionText;
...
>> localQuestion.questionText is not null.
...
>> questionCheckBoxWindow appears to have information in it in the debugger as well, and is not null.

Well, then it's rather clear: questionTextArea is null
meaning that text area is not created yet at the moment you try to execute that line of code.
0
 

Author Comment

by:csciguy81
ID: 24194369
Sure, but I'm making a new instance of that window before then.  That text area is a component of that window.  Shouldn't it be created the instance that I say "new" window?  If not, then how do I pause or wait for that creation to complete before I continue?
0
 
LVL 37

Expert Comment

by:zzynx
ID: 24194489
>> ... how do I pause or wait for that creation to complete before I continue?

questionCheckBoxWindow = new QuestionPlainTextWithCheckBoxes();
questionCheckBoxWindow.addEventListener(FlexEvent.CREATION_COMPLETE, function():void {

      // Do you stuff here
      questionCheckBoxWindow.questionTextArea.text = localQuestion.questionText;

});
0
 
LVL 37

Assisted Solution

by:zzynx
zzynx earned 250 total points
ID: 24194585
Even better:

questionCheckBoxWindow = new QuestionPlainTextWithCheckBoxes();
questionCheckBoxWindow.addEventListener(FlexEvent.CREATION_COMPLETE, function onComplete():void {

      // Do you stuff here
      questionCheckBoxWindow.questionTextArea.text = localQuestion.questionText;
      ...


      questionCheckBoxWindow.removeEventListener(FlexEvent.CREATION_COMPLETE, onComplete); // Remove the listener again once it is triggered
});
0
 

Author Comment

by:csciguy81
ID: 24196460
No luck with this.  The event never seems to happen.  Ideas?

I see "created new" in my console.  I never see "inside this event".
questionCheckBoxWindow = new QuestionPlainTextWithCheckBoxes();
	trace("created new");
				questionCheckBoxWindow.addEventListener(FlexEvent.CREATION_COMPLETE, function onComplete():void {
	trace("inside this event");
	GetQuestionAndDisplay();
							      questionCheckBoxWindow.removeEventListener(FlexEvent.CREATION_COMPLETE, onComplete); // Remove the listener again once it is triggered
});

Open in new window

0
 
LVL 37

Accepted Solution

by:
zzynx earned 250 total points
ID: 24202162
All right, that indicates that the creation already did complete.
The code should have been:

questionCheckBoxWindow = new QuestionPlainTextWithCheckBoxes();
if (questionCheckBoxWindow.initialized) {
     trace("questionCheckBoxWindow is initialized");
     trace("questionCheckBoxWindow.questionTextArea = " +  questionCheckBoxWindow.questionTextArea );
     GetQuestionAndDisplay();
} else {
     questionCheckBoxWindow.addEventListener(FlexEvent.CREATION_COMPLETE, function onComplete():void {
              trace("inside this event");
              GetQuestionAndDisplay();
              questionCheckBoxWindow.removeEventListener(FlexEvent.CREATION_COMPLETE, onComplete); // Remove the listener again once it is triggered
     });
}

But probably you'll get a nullpointer again.

And the code of QuestionPlainTextWithCheckBoxes is the one you already posted?
This one?

<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:Controllers="Controllers.*" layout="absolute" width="740" height="530">
        <Controllers:QuestionController id = "questionController"/>
       
        <mx:TextArea id="questionTextArea" x="10" y="10" width="700" height="274" fontSize="12" editable="false" enabled="true" wordWrap="true" creationComplete="Init();"/>
        <mx:Tile direction="horizontal" x="10" y="292">
        and so on...
0
 

Author Comment

by:csciguy81
ID: 24216537
Okay, doing what you posted there got rid of the null pointer, however the main problem still exists.

If I do the following, here are the results.
Display a question with 4 answers.  Submit and display the second question with 4 answers.  Press previous and display the previous question with 4 answers.

Despite creating a new instance of the panel, the radio button group has a count of 12 items in it.  How do I "clear" the group each time the panel is redrawn?
0
 

Author Comment

by:csciguy81
ID: 24216757
Actually, that didn't fix it either.  I didn't save the changes.

The traces for:

 trace("questionCheckBoxWindow is initialized");
     trace("questionCheckBoxWindow.questionTextArea = " +  questionCheckBoxWindow.questionTextArea );

and

trace("inside this event");

Are never displayed.
0
 

Author Comment

by:csciguy81
ID: 24224549
I've ended up rewriting this entire portion of the app with another approach and it appears to be working like I want it to now.  Thanks for all the help.
0
 
LVL 37

Expert Comment

by:zzynx
ID: 24226083
>> Thanks for all the help.
... and you delete the question? Not even a C grade...
0
 

Author Comment

by:csciguy81
ID: 24226102
I accepted my last comment as the solution, but told them to put in partial points if possible.  Everything provided was helpful, but it didn't address the original issue I was having with maintaining the states of those buttons.
0
 
LVL 37

Expert Comment

by:zzynx
ID: 24226210
>> I accepted my last comment as the solution, but told them to put in partial points if possible.
You can do that yourself by marking different comments as "assisted solutions" (cf. the help here @ EE)
I'm going to "object" and that will un-close the question again. Then you can do as I told.
0
 
LVL 37

Expert Comment

by:zzynx
ID: 24226230
I quote from the help page:

Can I split the points?

You split the points. Each comment box has a button that says Accept Multiple Solutions. Click that, and you will see a page that allows you to assign points to any of the comments in the thread. There is a grade box at the bottom of the page.

Note: The total of the point splits must equal the original amount you assigned to the question, and no comment can receive fewer than 20 points. The Comment that was posted first is the Accepted Solution, and the rest of the comments are Accepted Solutions.
0
 
LVL 37

Expert Comment

by:zzynx
ID: 24226246
... and I see there's an error in that. The last sentence should read:
The Comment that was posted first is the Accepted Solution, and the rest of the comments are ***Assisted*** Solutions.
0
 

Author Closing Comment

by:csciguy81
ID: 31568798
Good help throughout.  I ended up re-writing this portion of the app.  Each question rebuilds the radiobuttons and groups, and if a previous answer was selected, then that button is selected when recreated.  Seems to work fine.
0

Featured Post

Free Tool: Postgres Monitoring System

A PHP and Perl based system to collect and display usage statistics from PostgreSQL databases.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

First things first - Preparation We need all the part for this install and it's much nicer to have them all on hand when you need them so here's what's required. Download Eclipse 3.5 32 bit (I like the Classic flavour) from here. (http://www.e…
I have found that much of my time doing support ends up being a constant repetition of the same steps to different people.  Early on I stated using web pages with Frequently Asked Questions (FAQs) to alleviate most of the burden.  Sometimes this jus…
The goal of the tutorial is to teach the user how to set there setting in Adobe Flash Media Live Encoder and YouTube for optimal video and audio quality.
The goal of the tutorial is to teach the user how to use the auto adjust feature and what the different options do. When your video is not working right you can choose the auto adjust feature to help choose your settings.

809 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question