Tuesday, February 2, 2021

dynamic datatable components in primefaces

I'm making notes about this here for future reference. I have an JSF application where I'm using a primefaces datatable component. 

One thing that I don't love about the current approach is that the rows in the table are domain objects in the application, and each column therefore must be a property of the object in order to display the column values. Some of the columns I want to display in the table are not naturally properties of the underlying domain object, but just temporary values associated with the task at hand. So to add them as table columns, I have to define them as properties of the object and I don't always like that. 

 More than once I've wondered if it would be better to just use a list of strings as each row in the table. This way, some of the columns might map to properties of the domain object, while others are temporary task-specific values. Anyway, I'm stashing a couple of notes here about how to do this in case I want to experiment with it later. 

Here is a snippet for displaying lists of strings as the rows for the datatable based on this thread

                            <p:dataTable id="#{rootViewId}TableContent"
                                         value="#{wizardController.rows}"
                                         rendered="true"
                                         var="row">    

                                <p:columns value="#{wizardController.rows[0]}" var="column" columnIndexVar="i">
                                    #{row[i]}
                                </p:columns>

And here is a second thread for displaying dynamically selected columns. This still uses properties on the domain object, but I wonder if I could retrieve the values from some sort of map associated with the domain object instead? Here is a snippet of the view components from that thread: 

<h:form>
    <p:selectCheckboxMenu value="#{employeeBean.selectedColumns}"
                          label="Table Columns">
        <f:selectItems value="#{employeeBean.columnMap.entrySet()}"
                       var="entry"
                       itemValue="#{entry.key}"
                       itemLabel="#{entry.value}"/>

        <p:ajax event="change" update="table"/>
    </p:selectCheckboxMenu>
    <br/>

    <p:dataTable id="table" var="emp" value="#{employeeBean.employeeList}">
        <p:columns value="#{employeeBean.selectedColumns}" var="colKey">
            <f:facet name="header">
                <h:outputText value="#{employeeBean.columnMap[colKey]}"/>
            </f:facet>
            <h:outputText value="#{emp[colKey]}"/>
        </p:columns>
    </p:dataTable>
</h:form>
And the corresponding bean code:
@ManagedBean
@ViewScoped
public class EmployeeBean {
    private List<String> selectedColumns = new ArrayList<>();
    private List<Employee> employeeList = new ArrayList<>();
    private Map<String, String> columnMap = new LinkedHashMap<>();

    @PostConstruct
    private void postConstruct() {
        initColumnProperties();
        initEmployeeList();
    }

    private void initColumnProperties() {
        addColumn("id", "ID");
        addColumn("name", "Name");
        addColumn("phoneNumber", "Phone Number");
        addColumn("address", "Address");
        selectedColumns.addAll(columnMap.keySet());
    }

    private void addColumn(String propertyName, String displayName) {
        columnMap.put(propertyName, displayName);
    }

    private void initEmployeeList() {
        DataFactory dataFactory = new DataFactory();
        for (int i = 1; i < 20; i++) {
            Employee employee = new Employee();
            employee.setId(i);
            employee.setName(dataFactory.getName());
            employee.setPhoneNumber(String.format("%s-%s-%s", dataFactory.getNumberText(3),
                    dataFactory.getNumberText(3),
                    dataFactory.getNumberText(4)));
            employee.setAddress(dataFactory.getAddress() + "," + dataFactory.getCity());
            employeeList.add(employee);
        }
    }

    public List<Employee> getEmployeeList() {
        return employeeList;
    }

    public List<String> getSelectedColumns() {
        return selectedColumns;
    }

    public void setSelectedColumns(List<String> selectedColumns) {
        this.selectedColumns = selectedColumns;
    }

    public Map<String, String> getColumnMap() {
        return columnMap;
    }
}

No comments: