0

I'm designing a form maker in which the users can add multiple options to their questions. So there's an add new option button and a remove button to every option. I want to use a dataTable in my Facelets page and bind it with an HtmlDataTable in my managed bean in order to add/delete options dynamically because we don't know how many options they'd like to add. So what I did is that the value of my dataTable is a List of Strings. My input texts (options) are added and deleted and when they add the whole question they are stored correctly in my DB. But every time the add or delete button is pressed my options are all empty so the user has to enter them again. My code is a bit lengthy so I'll provide you with a simplified version of them.

On my Facelets page I have:

<h:dataTable value="#{questionMaker.selectOneOptionList}" var="option" binding="#{questionMaker.table}"> 
<h:column>                                                                                     
<h:inputText value="#{option}"/>
<h:commandLink value="remove option" action="#{questionMaker.removeRadioButtonOption}"/>                                           
</h:commandLink>
</h:column>
</h:dataTable>

And in my managed bean the above variables are:

private HtmlDataTable table;
private List<String> selectOneOptionList;

they have getters and setters too of course. and this is my add method:

public void addRadioButtonOption() {
    String option = new String();
    selectOneOptionList.add(option);

}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
disasterkid
  • 6,948
  • 25
  • 94
  • 179

1 Answers1

2

There are several (potential) problems in this approach:

  • In order to get this construct to work, the bean must be view scoped (not request nor session scope). In request scope, the selectOneOptionList would be freshly recreated on every single request. You don't want to have that. In session scope, the bean would be shared among all browser windows/tabs. You also don't want to have that.

  • The binding attribute runs during view build time. So if the #{questionMaker} is view scoped, then it becomes implicitly request scoped due to JSF issue 790.

  • As being an immutable object, the String doesn't have a setter method. The <h:inputText value="#{option}"/> will never be able to set the entered value.

So, to solve your problem, you have to:

  • Make sure that your bean is @ViewScoped.
  • Don't let the binding attribute point to a view scoped bean property.
  • Either replace String by a fullworthy javabean, or access the list items by index instead.

So, this should do (assuming that you still want to stick to List<String> for some reason):

<h:dataTable value="#{questionMaker.selectOneOptionList}" binding="#{table}"> 
  <h:column>                                                                                     
    <h:inputText value="#{questionMaker.selectOneOptionList[table.rowIndex]}"/>
    ...
  </h:column>
</h:dataTable>
<h:commandButton value="add" action="#{questionMaker.addRadioButtonOption}" />

with

@ManagedBean
@ViewScoped
public class QuestionMaker {

    private List<String> selectOneOptionList;

    @PostConstruct
    public void init() {
        selectOneOptionList = new ArrayList<String>();
    }

    public void addRadioButtonOption() {
        selectOneOptionList.add(new String());
    }

    public List<String> getSelectOneOptionList() {
        return selectOneOptionList;
    }

}

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • that sounds like a terrific solution @Balus. I'm going to test it right now. But could you also tell me how my table bean should look like? Does it have to extend/implement something? – disasterkid Oct 02 '12 at 13:47
  • 1
    No, just a simple `@ManagedBean` class. I updated the bean example to show the minimum necessary code. For another example, see also "CRUD frameworks" link. – BalusC Oct 02 '12 at 13:49
  • So my table bean will look the CRUD example with a list of Items. What is an Item though? – disasterkid Oct 02 '12 at 14:06
  • It's just the model. In your case perhaps `Question`? – BalusC Oct 02 '12 at 14:07
  • Working now @BalusC! You are a genius. – disasterkid Oct 02 '12 at 14:41
  • I set my HtmlDataTable object as the table that you mentioned, it's in the same bean as others but it's working. – disasterkid Oct 02 '12 at 14:42
  • But I also like to add that it worked with SessionScope as well. – disasterkid Oct 02 '12 at 14:53
  • 1
    Surely the session scope "works" when the page is been opened in a single window/tab. But as answered: *"In session scope, the bean would be shared among all browser windows/tabs. You also don't want to have that."*. So, open the same page in multiple windows/tabs and play around while switching between them. The results are unintuitive and confusing! It is therefore simply the wrong scope for the purpose. See also the first "see also" link. – BalusC Oct 02 '12 at 14:54
  • That's right but the thing is, I have a separate table in the same xhtml page that each time a new question is added to the database has to refresh and show all the current questions. when I used viewscoped for that purpose it threw a nullpointerexception and that's where i switched back to sessionscoped. thanks a bunch anyway. – disasterkid Oct 02 '12 at 15:08