how to get primefaces datatable columns order and width from ManagedBean

datatable, dynamic-columns, jsf-2, primefaces

Solution

you are mistaking something:

- do not nest `p:column` inside `p:columns`

DataTable.getColumns does not return what you expect:

public List<UIColumn> getColumns() {
    if(columns == null) {
        columns = new ArrayList<UIColumn>();
        FacesContext context = getFacesContext();
        char separator = UINamingContainer.getSeparatorChar(context);

        for(UIComponent child : this.getChildren()) {
            if(child instanceof Column) {
                columns.add((UIColumn) child);
            }
            else if(child instanceof Columns) {
                Columns uiColumns = (Columns) child;
                String uiColumnsClientId = uiColumns.getClientId(context);

                for(int i=0; i < uiColumns.getRowCount(); i++) {
                    DynamicColumn dynaColumn = new DynamicColumn(i, uiColumns);
                    dynaColumn.setColumnKey(uiColumnsClientId + separator + i);
                    columns.add(dynaColumn);
                }
            }
        }
    }

    return columns;
}

this returns a synthetic emulation: only `columnKey` per column, and, as you stated, the correct number of columns.

generally speaking, `p:columns` is ONE component and when you access it from controller layer, you have to refer to its DECLARATION (single component) instead of its REPRESENTATION (multiple columns)

- since you are backing `p:columns` with your `#{datatableBean.table}` you should already have all the information you are trying to get from DataTable component, in a reverse-engineered fashion.

update

oh, now i realize what you want to do. you have to use event listeners for that.

from PF showcase:

<p:dataTable var="car" value="#{tableBean.carsSmall}" widgetVar="cars" draggableRows="true">  

    <p:ajax event="colReorder" listener="#{tableBean.onColReorder}" update=":some:component" />  

    <p:ajax event="colResize" listener="#{tableBean.onColResize}" update=":some:component"/>    

    <!-- columns here -->  
</p:dataTable> 

listening for these events in backing bean and mantain these informations should solve your problem

update 2

whew, it was not simple at all.

due to Primefaces bug for dynamic column resizing (at least for my tries) and incomplete column reordering event implementation, this is what came out:

<h:form id="form">
    <h:panelGroup id="model">
        <ui:repeat var="column2" value="#{testBean.columnList2}">
            <div>#{column2.title} - #{column2.width}</div>
        </ui:repeat>
    </h:panelGroup>

    <p:dataTable var="elem" value="#{testBean.elementList}" resizableColumns="true" draggableColumns="true">
        <p:ajax event="colReorder" listener="#{testBean.onColumnReorder}" update=":form:model" />  
        <p:ajax event="colResize" listener="#{testBean.onColumnResize}" update=":form:model"/>  

        <c:forEach var="column" items="#{testBean.columnList}">
            <p:column headerText="#{column.title}" rendered="#{column.visible}" width="#{column.width}">
                <f:attribute name="myCol" value="#{column}"/>
                <span>#{elem[column.property]}</span>
            </p:column>
        </c:forEach>
    </p:dataTable>
</h:form>

and this bean:

@ManagedBean
@ViewScoped
public class TestBean implements Serializable
{
    private static final long serialVersionUID = 1L;

    private List<MyColumn> columnList;
    private List<MyColumn> columnList2;
    private List<MyElement> elementList;

    public class MyColumn
    {
        private String title;
        private String property;
        private boolean visible = true;
        private boolean resizable = true;
        private int width = 100;

        public MyColumn(String title, String property, boolean visible, boolean resizable, int width)
        {
            super();
            this.title = title;
            this.property = property;
            this.visible = visible;
            this.resizable = resizable;
            this.width = width;
        }

        // getters and setters
    }

    public class MyElement
    {
        private String code;
        private String name;
        private String type;

        public MyElement(String code, String name, String type)
        {
            super();
            this.code = code;
            this.name = name;
            this.type = type;
        }

        // getters and setters
    }

    @PostConstruct
    public void init()
    {
        columnList = new ArrayList<>();
        columnList.add(new MyColumn("element code", "code", true, true, 100));
        columnList.add(new MyColumn("element name", "name", true, true, 100));
        columnList.add(new MyColumn("element type", "type", true, true, 100));

        columnList2 = new ArrayList<>(columnList);

        elementList = new ArrayList<>();
        elementList.add(new MyElement("001", "foo", "normal"));
        elementList.add(new MyElement("002", "bar", "strange"));
        elementList.add(new MyElement("003", "xyz", "normal"));
        elementList.add(new MyElement("004", "abc", "nothing"));
    }

    public void onColumnResize(ColumnResizeEvent event)
    {
        UIColumn column = event.getColumn();
        int width = event.getWidth();

        UIComponent colComponent = (UIComponent) column;
        MyColumn myCol = (MyColumn) colComponent.getAttributes().get("myCol");

        myCol.setWidth(width);
    }

    public void onColumnReorder(AjaxBehaviorEvent event)
    {
        DataTable table = (DataTable) event.getSource();

        columnList2.clear();

        for(UIColumn column : table.getColumns())
        {
            UIComponent colComponent = (UIComponent) column;

            MyColumn myCol = (MyColumn) colComponent.getAttributes().get("myCol");
            columnList2.add(myCol);
        }
    }

    // getters and setters

}

updated code, you can find ordered columns in `columnList2`. now are also shown in the form. now you can use `columnList2` to store oreder and widths to db.

Problem

i'm using primefaces 4.0, JSF Mojarra 2.2.2 and here is my datatable code : ``` <p:dataTable id="tabexam" paginatorPosition="bottom" var="exam" value="#{dyna.examViewDataModel}" widgetVar="examTable" emptyMessage="aucun résultat trouvé pour votre recherche" paginator="true" rows="40" selection="#{dyna.selectedExamen}" filteredValue="#{dyna.filteredexams}" selectionMode="single" resizableColumns="true" draggableColumns="true" paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}" rowsPerPageTemplate="40,80,120"> <p:columns value="#{datatableBean.table}" var="column" headerText="#{column.userListname}" rendered="#{column.visible}" resizable="#{column.resizable}" width="#{column.width}" columnIndexVar="colIndex"> <h:panelGroup> <p:outputLabel value="#{column.dbname}" styleClass="fonty"/> </h:panelGroup> </p:columns> </p:dataTable> ``` Here what I've tried so far, but I get empty Strings for getHeaderText in the console and empty width too, i wonder what i'm missing ``` UIViewRoot view = FacesContext.getCurrentInstance().getViewRoot(); DataTable tabler = (DataTable) view.findComponent(":form1:tabexam"); List<UIColumn> coler = tabler.getColumns(); for (int i = 0; i < coler.size(); i++) { System.out.println("/////////////////"); System.out.println(coler.get(i).getValueExpression("headerText").getValue(FacesContext.getCurrentInstance().getELContext())); System.out.println(coler.get(i).getHeaderText()); System.out.println(coler.get(i).isRendered()); System.out.println(coler.get(i).isResizable()); System.out.println(coler.get(i).getWidth()); System.out.println("/////////////////"); } System.out.println(coler.size()); ``` Note that `coler.size()` gives the number of columns shown on the datatable. but the `coler.get(i).getHeaderText()` always gives empty String.

Original source