Table Header in PDFs with Visualforce

by Patrick Connelly posted on November 15, 2016

One of the problems I had with the way that we generated the PDFs in previous Battle Station Invoice posts was that the table header wasn’t repeated for long lists of supplies or resources that continued on the next page. There’s a simple way to add the table header for PDFs generated in Salesforce using the flying saucer mark-up but that won’t generate the table header correctly for us. It seems that the -fs-table-paginate tag does not play well when combined with a Visualforce component so we’ll need to take a bit more of a native CSS approach.

If you are doing this with plain Visualforce and apex:pageBlockTable, the -fs-table-paginate is the way to go.

Updating Table Header in Visualforce

The current version of the page does not have the table head separated from the table body. So the first thing we’ll need to do is to add that.

<div class="sectionHeader">{!$ObjectType.Resource__c.labelPlural}</div>
<table id="resources">
    <thead>
        <tr>
            <apex:repeat value="{!$ObjectType.Resource__c.FieldSets.Battle_Station_Invoice}" var="f">
                <th class="tableHeader resource_{!$ObjectType.Resource__c.fields[f].Name}">{!$ObjectType.Resource__c.fields[f].Label}</th>
            </apex:repeat>
        </tr>
    </thead>
    <tbody>
        <apex:repeat value="{!station.Resources__r}" var="resource">
            <tr>
                <apex:repeat value="{!$ObjectType.Resource__c.FieldSets.Battle_Station_Invoice}" var="f">
                    <td class="resource_{!$ObjectType.Resource__c.fields[f].Name}"><apex:outputField value="{!resource[f]}"/></td>
                </apex:repeat>
            </tr>
        </apex:repeat>
    </tbody>
</table>

<div class="sectionHeader">{!$ObjectType.Supply__c.labelPlural}</div>
<table id="supplies">
    <thead>
        <tr>
            <apex:repeat value="{!$ObjectType.Supply__c.FieldSets.Battle_Station_Invoice}" var="f">
                <th class="tableHeader supply_{!$ObjectType.Supply__c.fields[f].Name}">{!$ObjectType.Supply__c.fields[f].Label}</th>
            </apex:repeat>
        </tr>
    </thead>
    <tbody>
        <apex:repeat value="{!station.Supplies__r}" var="supply">
            <tr>
                <apex:repeat value="{!$ObjectType.Supply__c.FieldSets.Battle_Station_Invoice}" var="f">
                    <td class="supply_{!$ObjectType.Supply__c.fields[f].Name}"><apex:outputField value="{!supply[f]}"/></td>
                </apex:repeat>
             </tr>
        </apex:repeat>
    </tbody>
</table>

Make it prettier

Now that we’ve got our thead and tbody delineation, we can now add our style. To do this we’ll simply modify the @page annotation in our CSS to tell it to repeat the header on every new page.

@page {
    <apex:outputPanel layout="none" rendered="{!station.Project_Status__c != 'Complete'}">
        background: url("{!$Resource.BattleStationDraft}") no-repeat center center;
    </apex:outputPanel>

    @bottom-left {
        content: element(footer);
    }

    thead {
        display: table-header-group;
    }
}

Now the header will be repeated for every page the table exists on

The completed Visualforce page with all the upadtes can be seen here.