Solved

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

Posted on 2009-04-09
39
410 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
 
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
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 

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

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

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

This is intended to introduce all collision detection principles in flash, their strengths, weaknesses and workarounds. The main method for Collision Detection in flash is using hitTestObject. But unless you'll be pushing rectangular shapes without …
There are times in your Flash CS4 application when you want more than a simple pointer or a hand, and it's hard to find an ideal walk-through to tell you what to do.  I spent a few days recently learning my way around making custom cursors in Flash,…
In this tutorial viewers will learn how to create a basic motion tween animation in Flash Open a new document in Flash: Draw/import an image: Press CTRL + F8 to convert it into a graphic symbol: Select a frame (how long you want the tween to last): …
The goal of the tutorial is to teach the user what frame rate is, how to control it and what effect it has on the video.

919 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

15 Experts available now in Live!

Get 1:1 Help Now