Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

so, say I have the following map:

public Map<String1, Map<String2, Object>> objectMap {get; set;}
public List<String> ListofString1Entries {get; set;}
public List<String> ListofString2Entries {get; set;}

and I want to render it in the following table:

String 1
    String 2
        Object.Name    Object.Division
        Object.Name    Object.Division
        Object.Name    Object.Division
String 1
    String 2
        Object.Name    Object.Division
        Object.Name    Object.Division
        Object.Name    Object.Division

(imagine that the above is a pageBlockTable).

I have the following visualforce code:

<apex:pageBlock mode="edit" id="results">
  <apex:repeat value="{!ListofString1Entries}" var="i">
    <div id="head" style ="height: 30px; background-color:#66CCFF;width:100%;text-align:center; font-size:20px;">{!i}</div>
      <apex:pageBlockTable value="{!objectMap[i]}" var="d">
        <apex:repeat value="{!ListofString2Entries}" var="j">
           <apex:pageBlockTable value="{!d[j]}" var="c">

             <apex:column style="width:10%">
                <apex:facet name="header">
                   <apex:outputField value="Name" />
                </apex:facet>
                <apex:outputField value="{!c.Name}"/>
             </apex:column>
             <apex:column style="width:10%">
                <apex:facet name="header">
                  <apex:outputField value="Name" />
                </apex:facet>
                <apex:outputField value="{!c.Name}"/>
             </apex:column>
           </apex:pageBlockTable>
         </apex:repeat>
       </apex:pageBlockTable>
     </apex:repeat>                
   </apex:pageBlock>

I keep getting this error:

Unknown property 'String.Account' Error is in expression '{!d[j]}' in component in page testcontacts Error evaluating dynamic reference 'Account'

but I'm not referencing the account object at all... I'm referencing the User object.

Anyone have any ideas on this?

share|improve this question
    
Are you using the lists of strings to explicitly maintain the order of the keys retrieved from the map? –  Mark Pond Feb 19 '13 at 20:21
    
Nope, I'm just using the list of strings to make the keysets accessible... is there a way to access the keysets directly through VF without having to use String lists –  gfppaste Feb 19 '13 at 20:54
    
Yes, there is a way to access the keyset without using string lists. Iterating over a map collection is iterating the keyset. No lists required if order is not critical. Updating my answer with the keyset version of the example code. –  Mark Pond Feb 19 '13 at 21:57

6 Answers 6

up vote 5 down vote accepted

I would first suggest cutting down your VF markup to the bare minimum until you get the rendered structure that you are trying to achieve. This will keep you from troubleshooting both the rendering from nested maps and related VF oddities (like nested pageblocktable header facets and rendering dynamic header text within the 2nd level).

Also if you don't need to maintain an explicit order of data output while rendering from the maps, you might look into using the keyset instead of an ancillary list of keys. If order is critical, lists are the way to go.

Since your example doesn't provide actual data structures being used nor specific expected data output, I mocked some up. This code is functional, the first VF pageblock outputs UL and LI tags, second outputs pageblocktable markup.

Controller:

public with sharing class NestedMapController {

    public Map<String, Map<String, myObject>> objectMap {get; set;}
    public List<String> ListofString1Entries {get; set;}
    public List<String> ListofString2Entries {get; set;}

    public class myObject {
        public String Name { get; set; }
        public String Division { get; set; }

        public myObject(String name, String division) {
            this.Name = name;
            this.Division = division;
        }
    }

    public NestedMapController() {
        // build the string lists
        ListofString1Entries = 'String1-Key1,String1-Key2'.split(',');
        ListofString2Entries = 'String2-Key1,String2-Key2'.split(',');

        // build the map data
        objectMap = new Map<String, Map<String, myObject>>();
        objectMap.put('String1-Key1', new Map<String, myObject>{'String2-Key1' => new myObject('Name1', 'Division1'), 
                                                             'String2-Key2' => new myObject('Name2', 'Division2')});

        objectMap.put('String1-Key2', new Map<String, myObject>{'String2-Key1' => new myObject('Name3', 'Division3'), 
                                                             'String2-Key2' => new myObject('Name4', 'Division4')});

    }
}

Page:

<apex:page controller="NestedMapController" >
    <apex:pageBlock id="results2">
        <ul>
            <apex:repeat value="{!ListofString1Entries}" var="i">
                <li>outer key: {!i}</li>
                    <ul>
                        <apex:repeat value="{!ListofString2Entries}" var="j">
                            <li>inner key: {!j}</li>
                            <ul>
                                <li>{!objectMap[i][j]}</li>
                            </ul>
                        </apex:repeat>
                    </ul>
            </apex:repeat>
        </ul>
    </apex:pageBlock>

<apex:pageBlock id="results3">
    <ul>
        <apex:repeat value="{!objectMap}" var="outerKey">
            <li>outer key: {!outerKey}</li>
                <ul>
                    <apex:repeat value="{!objectMap[outerKey]}" var="innerKey">
                        <li>inner key: {!innerKey}</li>
                        <ul>
                            <li>{!objectMap[outerKey][innerKey]}</li>
                        </ul>
                    </apex:repeat>
                </ul>
        </apex:repeat>
    </ul>
</apex:pageBlock>

    <apex:pageBlock mode="edit" id="results">
      <apex:repeat value="{!ListofString1Entries}" var="i">
        <div id="head" style ="height: 30px; background-color:#66CCFF;width:100%;text-align:center; font-size:20px;">{!i}</div>      
            <apex:repeat value="{!ListofString2Entries}" var="j">
                <div class="bold">{!j}</div>
                   <apex:pageBlockTable value="{!objectMap[i][j]}" var="c"> 

                     <apex:column style="width:10%">
                        <apex:facet name="header">
                           <apex:outputText value="Name" />
                        </apex:facet>
                        <apex:outputText value="{!c.Name}"/>
                     </apex:column>
                     <apex:column style="width:10%">
                        <apex:facet name="header">
                          <apex:outputText value="Division" />
                        </apex:facet>
                        <apex:outputText value="{!c.Division}"/>
                     </apex:column>
                   </apex:pageBlockTable>

             </apex:repeat>
         </apex:repeat>                
   </apex:pageBlock>


</apex:page>
share|improve this answer
    
Nice one Mark - you're right about the VF formatting too, the conditional rendering in the page wasn't playing ball. –  techtrekker Feb 19 '13 at 21:29

So I've come across this problem before (the one you've mentioned re not using lists), and have just the gist for this situation! (See https://gist.github.com/Oblongmana/2080f5bbf66c74341ae6 - but I've copied what you need below)

So assuming you have a

Map<String,Map<String,SObject>>

property in your controller (which you do!), this will go iterate over the whole shebang. I haven't put any formatting in other than a colon to separate items - I'll leave that to you. You can most likely adapt this to any repeating apex element - I've just used plan apex:repeat and outputText items for simplicity's sake.

Basically, the outer repeater goes over the outer map, and the var attribute is equal to String1 in your map. The inner repeater iterates over the inner map (which you look up by using the var from the outer repeater: "testMapKey" in this case). You can then access the SObject at the end of the trail by {!testMap[testMapKey][testMapNestedKey]}

<apex:repeat value="{!testMap}" var="testMapKey">  
    <apex:outputText value="{!testMapKey}"/>
    <apex:repeat value="{!testMap[testMapKey]}" var="testMapNestedKey">
        <apex:outputText value="{!testMapNestedKey}:"/>  
        <apex:outputText value="{!testMap[testMapKey][testMapNestedKey]}"/> 
    </apex:repeat>
</apex:repeat>
share|improve this answer

I reckon you will have to change you inner nested map to be a Wrapper class

public Map<String, List<NestedMap> > objectMap {get; set;}

class NestedMap{
String key;
Account value;
}
share|improve this answer
    
Agreed, wrapper class is the way to go here –  Ralph Feb 19 '13 at 20:22
    
A wrapper class is not necessary to retrieve values from the Map of Maps. In this scenario it just adds complication. To retrieve from the nested maps in VF you use array notation to retrieve data by the variable keys of the maps. For instance, within two nested repeaters (perhaps iterating the two maps' keysets) using var="i" & var="j" you would retrieve data from the nested map as objectMap[i][j]. –  Mark Pond Feb 19 '13 at 20:31

Few days ago I came across a situation where I have to show the below structure on VF page :

ProgramName

1Plan Name

Plan Record

2Plan Name

Plan Reocord

For this I have used Map<string,Map<String,List<Object>>>MapProgPlanRate and used it in the following way on the Visual force page . Map is get;set;

VF code to show above Map:

<apex:repeat value="{!MapProgPlanRate}" var="ProgNameKey">
<apex:outputText value="{!ProgNameKey}"/>
 <apex:repeat value="{!MapProgPlanRate[ProgNameKey]}" var="PlanNameKey">
  <apex:outputText value="{!PlanNameKey}"/>
   <apex:repeat value="{!MapProgPlanRate[ProgNameKey][PlanNameKey]}" var="lstGrnRate">
     <apex:outputText value="{!lstGrnRate.Name}"/>
   </apex:repeat>
 </apex:repeat>

Happy coding !!!

Ajay

share|improve this answer

Change d[j] to objectMap[i][d]

share|improve this answer
    
I tried that already, i keep getting "Error is in expression objectMap[i][j]", same with objectMap[d][j] –  gfppaste Feb 19 '13 at 19:16

Maybe try using a List of Lists and iterate through that. If you get the Account error, try using a custom wrapper object so there is no possible reference to account. This sort of thing worked for me.

<apex:repeat value="{!results}" var="fundwrapper">
<apex:repeat value="{!fundwrapper.fund}" var="fund">
    <div class="page">
                <apex:outputText value="{!fundwrapper.Date}" />
                <apex:outputText value="{!fund.Organization__r.Name}" />
                ...
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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