• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 421
  • Last Modified:

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

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
csciguy81
Asked:
csciguy81
  • 19
  • 19
3 Solutions
 
lexxwernCommented:
>> 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
 
zzynxSoftware engineerCommented:
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
 
csciguy81Author Commented:
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
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
zzynxSoftware engineerCommented:
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
 
csciguy81Author Commented:
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
 
zzynxSoftware engineerCommented:
>> 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
 
csciguy81Author Commented:
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
 
csciguy81Author Commented:
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
 
zzynxSoftware engineerCommented:
>> 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
 
csciguy81Author Commented:
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
 
zzynxSoftware engineerCommented:
>> 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
 
csciguy81Author Commented:
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
 
zzynxSoftware engineerCommented:
>> 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
 
csciguy81Author Commented:
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
 
zzynxSoftware engineerCommented:
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
 
csciguy81Author Commented:
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
 
zzynxSoftware engineerCommented:
>> 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
 
csciguy81Author Commented:
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
 
zzynxSoftware engineerCommented:
>> 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
 
csciguy81Author Commented:
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
 
zzynxSoftware engineerCommented:
Your problem will be solved if you recreate your questionCheckBoxWindow for every next/previous button click:

questionCheckBoxWindow = new QuestionCheckBoxWindow();
0
 
csciguy81Author Commented:
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
 
zzynxSoftware engineerCommented:
>> 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
 
csciguy81Author Commented:
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
 
zzynxSoftware engineerCommented:
>> 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
 
csciguy81Author Commented:
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
 
zzynxSoftware engineerCommented:
>> ... 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
 
zzynxSoftware engineerCommented:
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
 
csciguy81Author Commented:
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
 
zzynxSoftware engineerCommented:
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
 
csciguy81Author Commented:
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
 
csciguy81Author Commented:
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
 
csciguy81Author Commented:
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
 
zzynxSoftware engineerCommented:
>> Thanks for all the help.
... and you delete the question? Not even a C grade...
0
 
csciguy81Author Commented:
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
 
zzynxSoftware engineerCommented:
>> 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
 
zzynxSoftware engineerCommented:
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
 
zzynxSoftware engineerCommented:
... 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
 
csciguy81Author Commented:
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

Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

  • 19
  • 19
Tackle projects and never again get stuck behind a technical roadblock.
Join Now