1

I'm trying to filter a p:dataTable with Primefaces component p:selectCheckboxMenu in one column header. This however, does not work as intended. The other filters on the datatable work just fine, such as input fields. The column in question is the Room type, that has the p:selectCheckboxMenu.

The filtering works once, after that checking or unchecking boxes on the selectCheckbox menu doesn't add or remove any filtering on the table.

Here's an interesting bit on the problem:

If I remove the selectionMode="single" attribute from the datatable, then the sorting works even after the first checkBox toggle. As in, I can toggle and untoggle a box and the p:dataTable gets filtered accordingly. But, I need the selection mode here, since I'm supposed to be able to select a row and navigate to another view by clicking it. That doesn't work when there's no selectionMode attribute on the datatable.

Here's my datatable:

<div class="background">

        <div class="freeRoomsContent">

            <br/>
            <p:outputLabel value="free rooms" styleClass="headerfont"/>
            <br/>
    <h:form id="freeRoomsForm">
        <p:dataTable id="freeRoomsTable" var="room" paginatorPosition="bottom" paginatorAlwaysVisible="false"
                     value="#{freeRoomsController.freeRoomsList}" selectionMode="single" selection="#{freeRoomsController.room}"
                     rowKey="#{room.roomId}" widgetVar="freeRoomsTable"
                     paginator="true" rows="20" pageLinks="5" scrollable="false"
                     paginatorTemplate="{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
                     rowsPerPageTemplate="20,50,100" skipChildren="true" emptyMessage="No free rooms available.">
            <p:ajax event="rowSelect" listener="#{freeRoomsController.onRowSelect}" />
            <p:column headerText="Room Id" sortBy="#{room.roomId}" filterMatchMode="contains" filterBy="#{room.roomId}">
                <h:outputText value="#{room.roomId}"/>
              </p:column>

            <p:column headerText="Room number" sortBy="#{room.roomNumber}" filterMatchMode="contains" filterBy="#{room.roomNumber}">
                <h:outputText value="#{room.roomNumber}" />
            </p:column>
<!-- other similar columns -->

            <p:column headerText="Room type" filterMatchMode="exact" filterBy="#{room.roomType}">
                <f:facet name="filter">
                    <p:selectCheckboxMenu  onchange="PF('freeRoomsTable').filter()"
                                     label="Room type">
                        <f:selectItems value="#{staticData.roomTypes}" var="rt" itemLabel="#{msg[rt.name]}" itemValue="#{rt.name}"
                                       />
                    <p:ajax event="change" process="@this" update="freeRoomsForm" />
                    <p:ajax event="toggleSelect" process="@this" update="freeRoomsForm" />
                    </p:selectCheckboxMenu>
                </f:facet>
                <h:outputText value="#{msg[room.roomtype.name]}">
                    <f:convertDateTime pattern="dd.MM.yyyy" />
                </h:outputText>
            </p:column>

<!-- normal input field columns that work -->

        </p:dataTable>
    </h:form>
</div>
</div>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Steve Waters
  • 3,348
  • 9
  • 54
  • 94
  • 1
    have you tried to **remove** p:ajax nested inside p:selectCheckboxmenu? i think filter() function itself fire ajax request. also, try to **add** p:ajax event=filter nested in datatable (e.g. below p:ajax for rowSelect) – Nikola Oct 03 '16 at 10:22

1 Answers1

3

This worked for me:

  • Using value and filteredValue in p:dataTable

  • Use only onchange=PF('freeRoomsTable').filter(), without child p:ajax elements in p:selectCheckboxMenu

  • Set value attribute for p:selectCheckboxMenu, e.g. value="#{someBean.selectedItems}"

  • In your bean, use private String[] selectedItems, with getter and setter

  • In your bean implement filterFunction, method need to have signature boolean (Object, Object, Locale) altough for this example only 1st argument (Object value) is used:
    public boolean filterFunction(Object value, Object filter, Locale locale) {
    
        // instanceof checking probably not needed
        if (value == null || !(value instanceof String)) {
            return true;  
        }
    
        String valueInRow = (String)value;
    
        // if nothing is selected, show row in table i.e. return true, 
        // you can play with this ofcourse
        if (selectedItems == null || selectedItems.length == 0) {
            return true;
        }
    
        // if item in row matches any of the items that were selected in header,    
        // show in table i.e. return true
        for (int i = 0; i < selectedItems.length; i++) {
            if (selectedItems[i].equals(valueInRow)) {
                return true;
            }
        }
    
        // if you don't want to show row in table, return false
        return false;
    
    }
    

  • In p:column, call filterfunction=#{someBean.filterFunction} - no arguments, no parenthesis, no need to use any filterMatchMode, leave only filterBy
  • Nikola
    • 554
    • 1
    • 4
    • 20
    • Thanks! Will try it tomorrow. I'll check your answer if it was correct. – Steve Waters Oct 04 '16 at 15:40
    • I did all the steps mentioned here. The filterFunction gets called as I toggle the boxes and that method gets as value parameter all the roomtypes that are showing on the freeRoomsTable. However, the selectedItems String[] array is just null all the time. Its getter and setter is not accessed after opening the view. The backing bean is @ViewScoped. I use the String array as you instructed: – Steve Waters Oct 05 '16 at 09:59
    • 1
      I can't tell you much further without Java code and the rest of page...try to change to SessionScoped just to see if it works. And i find it hard to belive getter and setter are not called at all. Also, i noticed you got skipChildren=true, try to remove that. – Nikola Oct 05 '16 at 15:46
    • 1
      You're a genius. Not sure why I ended up adding that skipChildren attribute in the first place. Probably some desperate workaround attempt at some point. Anyway, now it works! Except... the "select all" option :) It's not a biggie at this point. Any ideas? I'll check your answer correct anyway as you'll add "remove skipChildren" on it. – Steve Waters Oct 05 '16 at 18:48
    • I should probably create a separate question about that "select all" issue. – Steve Waters Oct 06 '16 at 05:30
    • 1
      yea i noticed also select all is problem...try with this: http://stackoverflow.com/a/24824162/1072089, i haven't tried it. in worst case scenario, you could have 2 consecutive ajax requests, 1st for toggling select all and doing nothing, and then 2nd for filtering. something like – Nikola Oct 06 '16 at 08:56
    • `@SessionScoped` is maybe to much, a compatible `@ViewScoped` should work. Remember there are 2 of each `@*Scoped`, one deprecated the other not. So if you use `@ManagedBean` which is deprecated, also use the **deprecated** `@*Scoped` family. Otherwise `@Named` and the **non-deprecated** family only works together. A very common mistake! – Roland Mar 27 '18 at 22:30
    • Also read @BalusC 's answer about scope annotations and which to choose. Link is coming up ... https://stackoverflow.com/questions/7031885/how-to-choose-the-right-bean-scope – Roland Mar 27 '18 at 22:32
    • @Nikola I wonder if there an interface for `filterFunction()` around? – Roland Mar 27 '18 at 22:34