Solved

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

Posted on 2009-04-09
39
407 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
Comment Utility
>> 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
Comment Utility
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
Comment Utility
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
 
LVL 37

Expert Comment

by:zzynx
Comment Utility
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
Comment Utility
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
Comment Utility
>> 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
Comment Utility
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
Comment Utility
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
Comment Utility
>> 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
Comment Utility
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
Comment Utility
>> 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
Comment Utility
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
Comment Utility
>> 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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
>> 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
Comment Utility
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
Comment Utility
>> 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
What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 

Author Comment

by:csciguy81
Comment Utility
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
Comment Utility
Your problem will be solved if you recreate your questionCheckBoxWindow for every next/previous button click:

questionCheckBoxWindow = new QuestionCheckBoxWindow();
0
 

Author Comment

by:csciguy81
Comment Utility
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
Comment Utility
>> 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
Comment Utility
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
Comment Utility
>> 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
Comment Utility
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
Comment Utility
>> ... 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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
>> Thanks for all the help.
... and you delete the question? Not even a C grade...
0
 

Author Comment

by:csciguy81
Comment Utility
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
Comment Utility
>> 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
Comment Utility
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
Comment Utility
... 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
Comment Utility
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

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

I know the transition can be hard. We got used to the ease of use ActionScript 2 had, but honestly, it became problematic later on, especially if designers were involved in the project and found it easy to add code as they saw fit. So, this artic…
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…
The goal of the tutorial is to teach the user how to select the video input device. Make sure you have an input device that in connected and work and recognized by Adobe Flash Media Live Encoder and select it in the “video input” menu.
The goal of the tutorial is to teach the user how to select which audio input to use. Once you have an audio input plugged into the laptop or computer, you will go into the audio input settings and choose which audio input you want to use.

771 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

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now