25

In a JSF dataTable I want to display the row index next to the rows... like:

Column A    Column B
1           xxx
2           yyy

I thought that I could use an implicit el variable like #{rowIndex} but this is not working.

A solution I found is to create a binding for the data table and use the binding like:

<h:dataTable var="item" value="#{controller.items}" binding="#{controller.dataTable}">
    <h:column>#{controller.dataTable.rowIndex}</h:column>
    <h:column>value</h:column>
</h:dataTable>

but this solution is complex and doesn't work well when I have many nested dataTables in a page.

Any ideas on how to solve this in a better way?

Panagiotis Korros
  • 10,840
  • 12
  • 41
  • 43
  • 2
    See [http://stackoverflow.com/questions/14633008/jsf-2-datatable-row-index-without-datamodel]. This answer is newer than those below - also short and sweet. – frIT Oct 02 '15 at 16:12
  • Does this answer your question? [JSF 2 dataTable row index without dataModel](https://stackoverflow.com/questions/14633008/jsf-2-datatable-row-index-without-datamodel) – Marco Sulla Aug 04 '21 at 10:54

6 Answers6

27

This solution was posted by Jamie Williams on CodeRanch. He says it works with Tomahawk. I'm using primefaces, which also supports it.

<t:dataTable rowIndexVar="row" value="#{someBean.value}">  
    <h:column>  
        <h:outputText value="#{row + 1}"/>  
    </h:column>  
</t:dataTable>
Brent Clay
  • 483
  • 5
  • 5
  • 4
    Ursula's comment: "Thank you for the solution using rowIndexVar. I'm using JSF 1.2 and myFaces, it only works without spaces in row+1 ``" – Peter O. Feb 23 '12 at 07:16
17

The existing solution does not strike me as a bad one. The rowIndex should work in nested tables so long as you're referencing the model of the nested table.

    <h:dataTable border="1" value="#{nestedDataModel}" var="nested">
        <h:column>
            <h:dataTable border="1" value="#{nested}" var="item">
                <h:column>
                    <h:outputText value="#{nested.rowIndex}" />
                </h:column>
                <h:column>
                    <h:outputText value="#{item}" />
                </h:column>
            </h:dataTable>
        </h:column>
    </h:dataTable>

Sample model:

public class NestedDataModel extends DataModel implements Serializable {

    private List<List<String>> nestedDataModel = populateModel();
    private int index;

    private List<List<String>> populateModel() {
        List<List<String>> list = new ArrayList<List<String>>();
        for(int x=0; x<3; x++) {
            List<String> nestedTableData = new ArrayList<String>();
            for(int y=0; y<3; y++) {
                nestedTableData.add("Foo x="+x+" y="+y);
            }
            list.add(nestedTableData);
        }
        return list;
    }

    @Override
    public int getRowCount() {
        return nestedDataModel.size();
    }

    @Override
    public Object getRowData() {
        List<String> list = nestedDataModel.get(index);
        return new ListDataModel(list);
    }

    @Override
    public int getRowIndex() {
        return index;
    }

    @Override
    public Object getWrappedData() {
        return nestedDataModel;
    }

    @Override
    public boolean isRowAvailable() {
        return index >= 0 && index < nestedDataModel.size();
    }

    @Override
    public void setRowIndex(int arg0) {
        index = arg0;
    }

    @Override
    public void setWrappedData(Object arg0) {
        throw new UnsupportedOperationException();
    }

}

Nesting dataTables should generally be avoided - if you're not careful (e.g. make them children of a form), this can lead to a O(N^2) pass over the table children for each phase of the lifecycle on a submit (and there are 6 phases in the lifecycle).


For something that is external to the model, you could use a simple counter in a managed bean:

public class RowCounter implements Serializable {

    private transient int row = 0;

    public int getRow() {
        return ++row;
    }

}

Config:

<managed-bean>
    <managed-bean-name>rowCounter</managed-bean-name>
    <managed-bean-class>tablerows.RowCounter</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
</managed-bean>

View:

<f:view>
    <h:dataTable border="1" value="#{tableDataBean.tableDataModel}"
        var="rowBean">
        <h:column>
            <h:outputText value="#{rowCounter.row}" />
        </h:column>
        <h:column>
            <h:outputText value="#{rowBean}" />
        </h:column>
    </h:dataTable>
</f:view>

This works because the bean is request-scope and bound to a read-only control outside a form. It would not work in a nested dataTable unless you wanted the row counter to be global to the view. But then, I'm not convinced that the row index should be a function of the view.

For a nested dataTable, you would be better off providing the row index from the row bean. It gives you more control if you decide to do things like pagination over data sets too.

McDowell
  • 107,573
  • 31
  • 204
  • 267
  • 4
    Nice. Why is it this complicated to get the row number from JSF datatable? Do the people that maintain JSF hate all other developers and they're purposely trying to make it difficult? – Mr. Lance E Sloan Oct 11 '12 at 19:57
7

In RichFaces there's a solution similar to Brent's:

<rich:dataTable value="#{backingBean.list}" var="v" rowKeyVar="index">
    <rich:column>
        <f:facet name="header">Index</f:facet>
        <h:outputText value="#{index + 1}" />
    </rich:column>
    <rich:column>
        <f:facet name="header">Name</f:facet>
        <h:outputText value="#{v.name}" />
    </rich:column>
</rich:dataTable>
Yuri
  • 1,695
  • 1
  • 13
  • 23
4

This worked form me

<h:dataTable var="item" value="#{controller.items}">
    <h:column>#{controller.items.indexOf(item)}</h:column>
    <h:column>value</h:column>
</h:dataTable>
  • There is an issue with this solution: it only works when you have an appropriate equals-method, otherwise u will always get -1. And also every object has to be different. If you have several equal-objects in the data structure u will always get the first position (if its a list). – Christian Ullenboom Apr 10 '19 at 12:40
3

I simply depend on list and position of var on this list:

<h:column>
          <f:facet name="header">#</f:facet>
          #{bean.listValue.indexOf(varObject)+1}
</h:column>
Youans
  • 4,801
  • 1
  • 31
  • 57
1

Since JSF 2.0 (introduced in 2009) the current UIComponent instance in the view is available via implicit EL variable #{component}. See also What exactly is #{component} in EL?

The <h:dataTable> tag is backed by the UIData component which implements NamingContainer. So, using #{component.namingContainer} anywhere within the context of the <h:dataTable> will give you the concrete UIData instance, usually in flavor of HtmlDataTable class.

<h:dataTable value="#{['one', 'two', 'three']}" var="item">
    <h:column>This is the `UIData` component: #{component.namingContainer}</h:column>
</h:dataTable>

jakarta.faces.component.html.HtmlDataTable@cafebabe
jakarta.faces.component.html.HtmlDataTable@cafebabe
jakarta.faces.component.html.HtmlDataTable@cafebabe

The UIData class has in turn the getRowIndex() method, so this should suffice:

<h:dataTable value="#{['one', 'two', 'three']}" var="item">
    <h:column>Row index: #{component.namingContainer.rowIndex + 1}</h:column>
</h:dataTable>

1
2
3

Do note that it is zero based, hence the + 1.

No need for binding or 3rd party tags or any custom backing bean logic.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555