5

How can I dynamically find the last row of a dataset in apex:dataTable? My goal is to set the styleClass of the last row (non-footer) to 'last'...

I have an apex:variable that tracks the row number and I was thinking about comparing this to the query.size property, but wasn't sure exactly how to do this...

<apex:variable var="rowNum" value="{!1}"/>
<apex:dataTable value="{!displayResults}" var="result" styleClass="list" rowClasses="dataRow odd,dataRow even" headerClass="headerRow">
    <apex:column style="{!IF(rowNum == displayResults.size, 'last', '')}">
        <apex:outputText value="{!FLOOR(rowNum)}"/>
        <apex:variable var="rowNum" value="{!rowNum + 1}"/>
    </apex:column>
    <apex:repeat value="{!fields}" var="field">
        <apex:column styleClass="dataCell" onmousemove="this.parentNode.classList.add('highlight')" onmouseout="this.parentNode.classList.remove('highlight')">
            <apex:facet name="header">{!mapFieldLabel[field]}</apex:facet>
            <apex:outputField value="{!result[field]}" rendered="{!field != 'Name'}" />
            <apex:outputLink value="/{!result['Id']}" rendered="{!field == 'Name'}">
                <apex:outputField value="{!result[field]}" />
            </apex:outputLink>
        </apex:column>
    </apex:repeat>
</apex:dataTable>

In this example, I'd like to have the final row (row 7) with a styleClass="last"...

dataTable example

3

Another mechanism which can be used if you need more than the CSS selector that Matt supplied for last-child would be to use script in the page to add a class of last to the className attribute on the last row in the table body element.

The script targets the first tbody element in the table hierarchy and appends the string last to the end of the existing className attribute.

<apex:page controller="YourController">
    <apex:dataTable id="accountTable" value="{!Accounts}" var="a" styleClass="list" rowClasses="dataRow odd,dataRow even" headerClass="headerRow">
        <apex:column value="{!a.Id}" />
        <apex:column value="{!a.Name}" />
    </apex:dataTable>
    <script>
        (function(tableId) {
            var table = document.getElementById(tableId);
            if (table) {
                var tbody = table.tBodies[0];
                tbody.rows[tbody.rows.length - 1].className += ' last';
            }
        })('{!$Component.accountTable}');
    </script>
</apex:page>

Relevant Mozilla documentation about targeting the elements of an HTML Table: https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement

  • I did need to modify the $Component reference to $Component.thePage:accountTable and add id="thePage" to the apex:page tag to make this work. Thank you! I like the non-css (kind of) hack! – Nathan Williams Nov 27 '13 at 15:13
4

Have you tried doing the styling using CSS selectors rather than a class?

I think table.list tr:last-child might be what you're looking for:

/* .last */
table.list tr:last-child
{
  color: #f00;
}
  • 1
    I wish I could accept two answers since this one worked as well... – Nathan Williams Nov 27 '13 at 15:22
3

Here is one approach to this problem -- use an inner class in your Controller for displayResults. The inner class represents one row and includes a method that can return the styleclass for the 1st...n-1st rows versus the nth row.

Alternatively, you can have the inner class simply have a boolean method getIsLastRow() and let the VF page use an {!IF (result.isLastRow,'lastStyleclass','notLastStyleclass')} to select the styleclass if you want to keep styleclass names out of the controller.

This eliminates the apex:variable component and can be extended to provide all sorts of hints to the VF page about how to conditionally render fields or rows.

// Within your controller

 public List<DisplayResult> displayResults {get; private set;}
 displayResults = new List<DisplayResults> ();
 for (some loop vbl)  // populate the inner class, one row per whatever you are trying to display
   displayResults.add(new DisplayResult(this,theData); // 'theData' is however you want to pass name, stage, freezedate - if all in the same SObject, then theData is the sObject from the for loop

  // Inner class to manage results and dynamically set style
  public DisplayResult class {
   Integer rowNum;
   MyController myCtlr;
 // other fields to display go here

  public DisplayResult(MyController myCtlr, someType theData) {  // constructor
   this.rowNum = myCtlr.displayResults.size() + 1;
   this.myCtlr = myCtlr;
   // remember the other data you need
  }
 public String getStyle() {
   return this.rowNum == this.myCtlr.displayResults.size() ? 'lastStyleClass' : 'notlastStyleClass';
 }      
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.