jsfuirepeatselectmanycheckbox

Select many checkbox selected items return null when there is nested ui:repeat


I have a problem list. Each problem has a sub category and main category and also each sub category has a main category. I want to list the problems like above.

--Main category1 (1 is id)
----subcat1
------problem1
------problem2
----subcat2
------problem3

--main category2
----subcat3
------problem4
------problem5
----subcat4
------problem6

and so on.

The html part is here;

    <ui:repeat  var="mainCatvar" value="#{dprEdit.mainCategories}">
    <h:outputText value="#{mainCatvar.mainCatName}" styleClass="mainTitle" /><br></br>  
            <ui:repeat var="subCategory" value="#{mainCatvar.subCategories}">
            <h:outputText value="#{subCategory.subCatName}" styleClass="title" />
                <h:selectManyCheckbox value="#{dprEdit.selectedProblems}"  >
                <f:selectItems value="#{subCategory.problems}" var="problem" 
                itemValue="#{problem.problemName}" itemLabel="#{problem.problemName}" />                                    
                </h:selectManyCheckbox>
            </ui:repeat>
    </ui:repeat>
<p:commandButton action="#{dprCreate.save}" value="Save" update="form1" ajax="false" style="width:150px" />

Here is my backend code;

    public class MainCatDAO extends BaseDAO {   
        private final static String SELECT_ALL_MAIN_CATEGORIES ="SELECT * FROM TESTAE.MAINCATPF";
        public MainCatDAO(Connection connection) {      
            super(connection);
        }
        private List<MainCat> getTable(String query, Object... values) throws ClassNotFoundException, SQLException {
            List<MainCat> table = new ArrayList<MainCat>();
            List<Map<String, Object>> data = this.getData(query, values);
            for (int i = 0; i < data.size(); i++) {
                ArrayList<SubCat> subList = new ArrayList<SubCat>();    
                // subList'i tekrardan tanımlama yani clear etmeme sebebim; maincat'a subList'i set etmeme rağmen,
                //sonrasında subList'i clear edince maincat'ın subList'i de sıfırlanıyor. 
                //Bu yüzden subList'i her seferinde tekrar tanımlıyorum.
                Map<String, Object> row = new HashMap<String, Object>();
                MainCat maincat = new MainCat();
                row = data.get(i);
                maincat.setMainCatId(((BigDecimal) row.get("MAINCATID")).intValue());
                maincat.setMainCatName(((String) row.get("MAINCATNAM")).trim());

                String sorgu = "SELECT * FROM TESTAE.SUBCATPF WHERE MAINCATID='"+((BigDecimal) row.get("MAINCATID")).intValue()+"'";            

                List<Map<String, Object>> dataSub = this.getData(sorgu);
                for (int a = 0; a < dataSub.size(); a++) {
                    ArrayList<Problem> problemList = new ArrayList<Problem>();
                    SubCat subcat = new SubCat();
                    row = dataSub.get(a);
                    if (row.get("SUBCATNAME") == null)
                        subcat.setSubCatName("");   
                    else
                        subcat.setSubCatName((((String) row.get("SUBCATNAME")).trim()));
                    subList.add(subcat);

                    String sorgu2 = "SELECT * FROM TESTAE.PROBLEMPF WHERE SUBCATID='"+((BigDecimal) row.get("SUBCATID")).intValue()+"'";
                    List<Map<String, Object>> dataProb = this.getData(sorgu2);
                    for (int p = 0; p < dataProb.size(); p++) {
                        Problem problem = new Problem();
                        row = dataProb.get(p);
                        Integer problemId = ((BigDecimal) row.get("PROBLEMID")).intValue();

                        if (row.get("PRBLMNAME") == null)
                            problem.setProblemName("");
                        else
                            problem.setProblemName((((String) row.get("PRBLMNAME")).trim()));
                        problemList.add(problem);
                    }
                    subcat.setProblems(problemList);
                }
                maincat.setSubCategories(subList);
                table.add(maincat);
            }
            return table;
        }

        public List<MainCat> getmainCategories () throws ClassNotFoundException, SQLException {

            return getTable(SELECT_ALL_MAIN_CATEGORIES);
    }}


Here is my save function;

            @ManagedBean(name = "dprEdit")
            @ViewScoped
            public class DprEdit implements Serializable {
            private List<String> selectedProblems = new ArrayList<String>();
    private List<MainCat> mainCategories;
    public DprEdit() throws ClassNotFoundException, SQLException {
    mainCategories = new MainCatDAO(ConnectionManager.getConnection()).getmainCategories();}

            public String save() throws ClassNotFoundException, SQLException, ParseException {
                    DprDAO dprDAO = new DprDAO(ConnectionManager.getConnection());


                    for (int i = 0; i < selectedProblems.size(); i++) {
                        dpr.setFormId(dpr.getFormId());
                        DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
                        Date today = new Date();
                        formDate = formatter.parse(formatter.format(today));
                    dpr.setFormDate(formDate);
                    dpr.setFilledBy(username);
                    dpr.setProblemName(selectedProblems.get(i));
                    String problemName = selectedProblems.get(i);
                    ProblemDAO problemDAO = new ProblemDAO(ConnectionManager.getConnection());
                    Problem prob = problemDAO.getProblemId(problemName);
                    dpr.setProblemId(prob.getProblemId());
                        dprDAO.insertDetail(dpr);
                    }
            public List<String> getSelectedProblems() {
                    return selectedProblems;
                }

            public void setSelectedProblems(List<String> selectedProblems) {
                    this.selectedProblems = selectedProblems;
                }
    public Integer getMaincatid() {
            return maincatid;
        }

        public void setMaincatid(Integer maincatid) {
            this.maincatid = maincatid;
        }}

My problem model attributes;

private Integer problemId;
private String problemName;
private String subCategory;
private String mainCategory;

my subcategory model attributes;

private Integer subCatId;
private String subCatName;
private List<Problem> problems;

my main category model attributes;

private Integer mainCatId;
private String mainCatName;
private List<SubCat> subCategories;

So, the problem is that I click some problems on the list then I click the save button and I expect to that selectedProblem list is full of my clicked problems. However the selectedProblems list is empty!

Do you have any advice?

Thank you all.


Solution

  • Look closer at <h:selectManyCheckbox value>, particularly at the associated bean.

    <ui:repeat value="#{dprEdit.mainCategories}" var="mainCatvar">
        <ui:repeat value="#{mainCatvar.subCategories}" var="subCategory">
            <h:selectManyCheckbox value="#{dprEdit.selectedProblems}">
                <f:selectItems value="#{subCategory.problems}" />
    

    Basically, you're binding the selected values of all checkbox groups to one and same bean property. During processing the form submit, every iteration will override the submitted values of checkbox group of the previous iteration. If you've placed a debug breakpoint on the setSelectedProblems() setter, you'd have noticed that. This way you ultimately end up with the submitted values of the last checkbox group. Given the "return null" complaint, apparently you selected nothing there.

    This is definitely wrong. The <h:selectManyCheckbox value> should in some way be associated with the currently iterated #{subCategory}, not directly with the #{dprEdit} managed bean. One way is mapping it with #{subCategory} as key:

    <h:selectManyCheckbox value="#{dprEdit.selectedProblems[subCategory]}">
    

    With:

    private Map<SubCat, String[]> selectedProblems = new HashMap<>();
    

    Note that you can't use List<String> here as generic type information is erased during runtime and the default model value is String[] and there's no way of instructing JSF/EL otherwise. If you want to use a fixed type, you could consider moving the selectedProblems into SubCat so that you can keep using List<String>:

    <h:selectManyCheckbox value="#{subCategory.selectedProblems}">