2

I have a map of String(key) and Wrapper object(value) called TableView. The TableView has public property of list of a wrapper class object called DataViewRow.

The DataView wrapper class has a map of column names and their values (Map of String to Object).

I try to render the columns dynamically based on the Map inside the DataViewRow wrapper object but the headers are not displayed in this case.

enter image description here

It looks like this in the visualforce page:

<apex:pageblocktable value="{!map_tableToData['Adjustments'].Rows}" var="row">
    <apex:repeat value="{!row.Values}" var="col">
        <apex:column headervalue="{!col}">
            {!row.Values[col]}
        </apex:column>
    </apex:repeat>
</apex:pageblocktable>

TableView class:

public class TableView 
{
    public TableView(List<DataViewRow> rows)
    {
        this.Rows = rows;
    }

    public List<DataViewRow> Rows { get; set; }
}

DataViewRow class:

public virtual class DataViewRow 
{
    protected DataViewRow() { }

    public DataViewRow(List<String> lst_columns, String title)
    {
        //Intialize a new map.
        this.Values = new Map<String,Object>();

        for (String columnName : lst_columns)
        {
            this.Values.put(columnName, null);
        }

        this.Title = title;
    }

    public String Title { get; set; }

    public Map<String,Object> Values { get; set; }

    public Set<String> ColumnNames 
    {
        get
        {
            return this.Values.keySet();
        }
    }

    public virtual Boolean IsEmptyData()
    {
        return false;
    }
}

The values in the map are populated correctly. I've tried to render the column names in the column value and the values are as expected, but the headers are not displayed at all.

I would appreciate your help.

UPDATE:

It seems that there is a problem binding the map in the repeat and use its key inside the headerValue attribute of the column. I've assigned map_tableToData['Adjustments'].Rows[0].Values.keySet() to a different property in the controller because it is the same for all the rows in my case. I would be happy to get any better solution.

  • Can you please post a picture showing what you expect about header – Santanu Boral Dec 21 '16 at 14:01
  • Is the column name a String sObject in the first row of your data or is it something else? Seeing the actual structure of your wrapper class and or map might be helpful. – crmprogdev Dec 21 '16 at 15:00
  • I've just updated the question, I hope it is clearer. – Elad Khalo Dec 22 '16 at 7:13
  • @EladKhalo, did you find any workaround for this. me too facing same issues. Please help me if do you find any solution on this. – Shravan Boddula Mar 2 '17 at 6:47
  • @ShravanBoddula, I've changed my solution to work with sObjects instead of the map. Unfortunately, I haven't found a more elegant way to solve it. – Elad Khalo Mar 2 '17 at 9:15
1

Can confirm - you will not get the headers from the Map keys when using the <apex:repeat> tag to auto-generate columns from a List<Map> structure.

I have tried it with <apex:facet> tags, on both the table and the column; I have tried it with various formulas in the value="" and var="" attributes of the <apex:repeat> tag; I've even tried giving all the headers the same fixed value: <apex:column headervalue="Fred>. The headers just don't display.

This makes sense if you think about it - there is no guarantee that every Map in a List<Map> structure will have the same keys, so how does VF know which ones to use as the headers? To get around this, you will have to provide your own headers in a separate property of the VF page controller. These can be the actual keys from List<Map>[0] or whatever others you might choose.

Sample code is below for situation where I use the first row's key values as the headers.

Relevant controller code:

public List<Map<string, string>> myTable
{
    get
    {
        List<Account> accts = [select id, name, shippingcity, phone, fax from Account];
        List<Map<string, string>> aTable = new List<Map<string, string>>();
        for(account a : accts)
        {
            // Add account fields as strings.  The "field==null ? '' : field" ternary
            // is necessary to avoid a "Map Key Not Found" page error.
            // Not sure why that is, but it's irrelevant to current discussion.
            Map<string, string> aMap = new Map<string, string>();
            aMap.put('ID', a.id);
            aMap.put('Name', a.name);
            aMap.put('Shipping City', a.shippingcity == null ? '' : a.shippingcity);
            aMap.put('Phone', a.phone == null ? '' : a.phone);
            aMap.put('Fax', a.fax == null ? '' : a.fax);
            aTable.add(aMap);
        }
        return aTable;
    }        
}

public List<string> tableHeaders
{
    get { return new List<string>(myTable[0].keySet()); }   
}

Relevant page tags:

<apex:pageblocktable value="{!myTable}" var="rowMap" >
    <apex:repeat value="{!tableHeaders}" var="headerKey">
        <apex:column headerValue="{!headerKey}" value="{!rowMap[headerKey]}" />
    </apex:repeat>
</apex:pageblocktable>

You could also create a second Map<string,string> to obtain custom headers for your columns. The relevant code for that would be:

public Map<string, string> custHeaders
{
    get 
    { 
        return new Map<string, string> { 
            tableHeaders[0]=>'Fred', 
            tableHeaders[1]=>'Daphne', 
            tableHeaders[2]=>'Velma', 
            tableHeaders[3]=>'Shaggy', 
            tableHeaders[4]=>'Scooby'}; 
    }
}

and

<apex:pageblocktable value="{!myTable}" var="rowMap" >
    <apex:repeat value="{!tableHeaders}" var="headerKey">
        <apex:column headerValue="{!custHeaders[headerKey]}" value="{!rowMap[headerKey]}" />
    </apex:repeat>
</apex:pageblocktable>

Hope this helps!

Doug

-1

Header can be added as a facet element
Try this:

<apex:pageblocktable value="{!map_tableToData['Adjustments'].Rows}" var="row">  
    <apex:repeat value="{!row.Values}" var="col">
        <apex:column>
        <apex:facet name="header">
          <apex:outputText value=" {!row.Values[col]}"></apex:outputText>
        </apex:facet>
      </apex:column>
    </apex:repeat> 
</apex:pageblocktable>
  • It doesn't work either. I've already tried it a few times. – Elad Khalo Dec 22 '16 at 7:02
  • Try debugging the html using 'Inspect element' on the page to see how it is rendered as HTML(the table, etc.,).Apart from it, 1.Try hard coding the value of the column instead of dynamically getting it just to see some value as a header first.If successful, check the dynamic value, print it and debug it on the page. – Shalini SF Dec 22 '16 at 10:26
  • Thanks. No headers in the HTML too. I've tried to print the value itself and it looks as expected. It looks like SF has some strange problem getting the value from the map. – Elad Khalo Dec 22 '16 at 12:38
  • Yes, SF can go crazy sometimes :). Can you please try this:Declare a <apex:variable var="v" value="{!col}"/> and then use this variable v in the column header. <apex:column> <apex:facet name="header"> <apex:outputText value=" {!v}"></apex:outputText> </apex:facet> </apex:column> If column has some specific issue extracting value from map, then this should help. Print the value of v just to see its extracted correctly. – Shalini SF Dec 22 '16 at 21:52
  • I've tried this approach too, but it is not possible to use <apex:variable> inside a repeat element. – Elad Khalo Dec 25 '16 at 6:45

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.