[SalesForce] Using subclass in pageBlockTable – Headers not displaying

I want to use a pageBlockTable to display a grid of customers and their products, with each cell of the grid showing the quantity bought.

As the number of products may change, the grid needs to be dynamic. The only way that I've found to display and save the data for each row is to create a subclass acting as a data structure to display the customer name for each row along with a List to hold the products for that row.

Unfortunately, when I use this subclass the headers don't display. I have created a couple of examples pages which get their data from the same controller.

public with sharing class TestController {

    // Data for the first example
    public List<Account> accounts {get; set;}
    public List<Product2> products {get; set;}

    // Data for the second example
    public List<GridRow> gridRows {get; set;}

    public TestController() {
        accounts = new List<Account>();
        accounts.add(new Account(Name='One'));
        accounts.add(new Account(Name='Two'));
        accounts.add(new Account(Name='Three'));

        products = new List<Product2>();
        products.add(new Product2(Name='Product One'));
        products.add(new Product2(Name='Product Two'));
        products.add(new Product2(Name='Product Three'));

        gridRows = new List<GridRow>();

        for(Integer cust = 0; cust < 3; cust++) {
            GridRow row = new GridRow();
            row.acc = new Account(Name='Customer '+cust);
            List<Product2> prods = new List<Product2>();
            for(Integer p = 0; p < 3; p++) {
                prods.add(new Product2(Name='Product '+p));
            }
            row.products = prods;
            gridRows.add(row);
        }
    }

    public class GridRow {

        public Account acc {get; set;}
        public List<Product2> products = new List<Product2>();

        public List<Product2> getProducts() {
            return products;
        }       

    }
}

The first page uses lists that are members of the controller. The headers display fine for this page.

<apex:page controller="TestController">

    <apex:pageBlock >

        <apex:pageBlockTable value="{!accounts}" var="a">

            <apex:column headerValue="Customer">
                <apex:outputField value="{!a.Name}" />
            </apex:column>

            <apex:repeat value="{!products}" var="p">
                <apex:column headerValue="{!p.Name}">
                    <input type="text" />
                </apex:column>
            </apex:repeat>

        </apex:pageBlockTable>

    </apex:pageBlock>
</apex:page>

The second uses a list of the subclass object to display the rows of data. The headers won't display for this page.

<apex:pageBlock >

    <apex:pageBlockTable value="{!gridRows}" var="row">

        <apex:column headerValue="Customer">
            <apex:outputField value="{!row.acc.Name}" />
        </apex:column>

        <apex:repeat value="{!row.products}" var="p">
            <apex:column headerValue="{!p.Name}">
                <input type="text" />
            </apex:column>
        </apex:repeat>

    </apex:pageBlockTable>

</apex:pageBlock>

I have read this salesforce stackexchange post, but I'm not sure why it says that:

If you are using a List, there is an important gotcha with dynamic tables: header cells are not rendered.

What is the difference between using the List of Accounts and List of Product2s in the first working example and using the List of subclass instances with their Product2 Lists in the second example?

Best Answer

I'm not sure what's going on behind the scenes that allows it to work differently depending on what's in the List.

It isn't exactly elegant, but you could use JavaScript to create the row headers, if you really need to.

 <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"> </script>
 <script type="text/javascript">
     var $j = jQuery.noConflict();
     var colHeaders = [];
     var i = 0;
     <apex:repeat value="{!gridRows[0].products}" var="p">
         colHeaders[i] = '{!p.Name}';  
         i++
     </apex:repeat>

     $j(document).ready(function() {
         var headerRow = $j('.theTable thead tr');
         for (i = 0; i < colHeaders.length; i++) {
             headerRow.append('<th>' + colHeaders[i] + '</th>');
         }
     });
 </script>

Add a styleClass to your table so that it is easier to select with jQuery.

 <apex:pageBlockTable value="{!gridRows}" var="row" styleClass="theTable">
Related Topic