How to use SelectManyCheckbox with two ArrayList?

2019-02-25 01:48发布

问题:

I'm trying to implement a <p:selectManyCheckbox> but I'm having no success.

Now I have the following architecture:

Course - have many Disciplines
Discipline - belongs to none, one or many Courses.

In Course class I have two ArrayList<Discipline>:

public class CourseMBean{

    (...)
    // Stores all disciplines
    private static ArrayList<Discipline> allDisciplines;

    // Stores only the disciplines that's already associated with this course.
    private static ArrayList<Discipline> courseDisciplines;

    (get and set for the arraylists)
    (...)
}

All data comes from a MYSQL DB, but that isn't the question. Now I want create a new Course, so I don't have anything in courseDisciplines.

I want show allDisciplines in checkboxes, and want that when user select one checkbox, the object Discipline of this checkbox be added in courseDisciplines - and when unselect one checkbox, remove the discipline from the courseDsiciplines.

My JSF 2.0 code is following:

<p:selectManyCheckbox id="disciplines" value="#{courseMBean.allDisciplines}" layout="grid" columns="2">                                          
     <f:selectItems value="#{courseMBean.courseDisciplines}" />
</p:selectManyCheckbox>

This actually shows all disciplines without any selected checkboxes, what's right. But when I select some checkboxes and submit the form I try to print the elements inside courseDisciplines, and this don't show anything in console.

What I'm doing wrong?

回答1:

when I select some checkboxes and submit the form I try to print the elements inside courseDisciplines

As the courseDisciplines actually represents the available items not the selected items, it seems that you misunderstood some basic concepts around the UISelectMany and UISelectItem(s) components.

The <f:selectItem(s)> (from the UISelectItem(s) family) represent the available items. It are exactly those items which are shown in the UI and which the enduser has to choose from.

The value attribute of <p:selectManyCheckbox> (from the UISelectMany family, like <h:selectManyCheckbox> and <h:selectManyMenu>) represent the (pre)selected items. If this is null or empty during first display of the form, then nothing is preselected. Or if this contains some preselected items, then only those available items which are equal() will be preselected.

When the enduser has changed the selection in the UI and submits the form, then all selected items will end up in the value attribute of UISelectMany component. The UISelectItem(s) remains unchanged.

Here's a basic kickoff example:

<p:selectManyCheckbox value="#{bean.selectedItems}">
    <f:selectItems value="#{bean.availableItems}" />
</p:selectManyCheckbox>
<p:commandButton value="Submit" action="#{bean.submit}" />
<p:messages autoUpdate="true" />
private List<String> selectedItems; // +getter +setter
private List<String> availableItems; // +getter (no setter necessary!)

@PostConstruct
public void init() {
    availableItems = new ArrayList<String>();
    availableItems.add("one");
    availableItems.add("two");
    availableItems.add("three");
}

public void submit() {
    System.out.println("Selected items: " + selectedItems);
}

(all other <p:selectManyXxx> and <h:selectManyXxx> components work exactly the same)

When a complex Javabean object like Discipline comes into the picture, then you need to make sure that there's a Converter for that so that JSF can properly convert between it and String for usage in generated HTML output and as HTTP request parameter (HTML and HTTP namely can't pass around nor hold Java objects, but only character sequences which are in Java represented by String).

This is perhaps your root problem. You said that nothing is printed to the console on submit. But it could be as good the case that the whole submit() method is actually never being invoked. You're not explicit enough on this. If the whole action method is indeed never invoked (i.e. a debug breakpoint doesn't hit there, or another System.out.println() printing a static string is never shown in console), then you've actually most likely a conversion error. If you have used <h|p:message(s)> the right way, or have paid attention to server log about queued but undisplayed faces messages, then you should have noticed it.

In that case, you need to implement a Converter which converts between Discipline and String.

@FacesConverter(forClass=Discipline.class)
public class DisciplineConverter implements Converter {

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
        // Write code here to convert from String to Discipline.
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
        // Write code here to convert from Discipline to String.
    }

}

More than often the DB ID is being used as String representation. See also the section "Complex object as selected item" of this answer on a related question: How to populate options of h:selectOneMenu from database?