Search code examples
jsfprimefacesdatatablefilteringtableheader

p:dataTable doesn't render header after filtering and paging back


I need to keep filtered data when I navigate between pages. After entering data into a filter cell of a data table, it displays correct filtered rows. If last entered data in the filter cell doesn't match any data rows to display and when I navigate to another page and return back - column headers and filter cells are not rendered. There is only paginator are displayed. It works fine if last entered data in the filter cell matches some data rows. It work properly also if I don't leave the page with data table.

jsf code:

<p:dataTable id="tableId" var="intance" widgetVar="instance" 
        value="#{model.rows}" filteredValue="#{model.filteredRows}" 
        sortBy="#{model.sortBy}" sortMode="multiple" rows="5" 
        rowsPerPageTemplate="5, 15" paginator="true" paginatorPosition="bottom">
    <p:ajax event="filter" listener="#{model.onFilter}"/>
    <p:ajax event="sort" listener="#{model.onSort}"/>
    <p:columns id="columnId" value="#{instance.columnsData}" 
        var="instanceColumn" columnIndexVar="ind" rendered="true" 
        sortBy="#{instance.columnsData[ind]}" 
        filterBy="#{instance.columnsData[ind]}" filterMatchMode="contains" 
        filterValue="#{model.tableFilters[''.concat(ind)]}" 
        headerText="#{labels[ind]}">
            <h:outputText value="#{instance.columnsData[ind]}"/>
    </p:columns>
</p:dataTable>

java code:

public class Model {

    private List<MyRow> filteredRows;

    private List<MyRow> rows;

    private Map<String, String> tableFilters;

    public void onFilter(FilterEvent event) {
        tableFilters = event.getFilters();
        if (MapUtils.isEmpty(tableFilters)) {
            filteredRows = null;
        }
    }

    public void List<MyRow> getRows() {
        //retreve data from service and transform to List<MyRow>)
        return rows;
    }

    /* getters and setter */
}


public class MyRow {
    private List<Object> columnsData;
    /* getters and setters */
}

Any thoughts? The code looks fine to me and should work. I use primefaces 3.5.

Thanks


Solution

  • The root of the problem is value="#{instance.columnsData}" of p:columns. Using #{instance} variable of the p:dataTable in this case is a mistake.

    It works fine when page is loaded for a first time and when #{model.rows} is not empty.

    When the #{model.rows} is not empty and when we type a value in the filter cell that doesn't match data in #{model.rows}, there is displayed no rows but column headers and filter cells are rendered anyway because of partial rendering only the data content (table headers and filter cells was rendered during rendering the page).

    When we navigate to an another page and then return back to the page with our filtered rows in the data table, there is a new full rendering of the page. So in this case (#{model.filteredRows} is not null) it doesn't matter how much data #{model.rows} contain. #{model.filteredRows} will be used to render the data table content. So when primefaces renders data table, in my case there is no data to render at all because of #{model.filteredRows} is empty not null.

    Because I use #{instance} variable of the p:dataTable in p:columns, p:columns will render no columns - no column headers and filter cells to display.

    Solution: use separate field to contain columns info.

    //edited class Model
    public class Model {
        //add a field that contains columns count. For example:
        private List<Integer> columns = Collections.nCopies(5, 1);
    
       //another fields
    }
    

    Need to use value="#{model.columns}" instead of value="#{instance.columnsData}"

    <p:columns id="columnId" value="#{model.columns}" 
        var="instanceColumn" columnIndexVar="ind" rendered="true" 
        sortBy="#{instance.columnsData[ind]}" 
        filterBy="#{instance.columnsData[ind]}" filterMatchMode="contains" 
        filterValue="#{model.tableFilters[''.concat(ind)]}" 
        headerText="#{labels[ind]}">
        <h:outputText value="#{instance.columnsData[ind]}"/>
    </p:columns>