‘Map key null not found in map’ when using apex:pageBlockTable

I have a simple controller extension with has a method which returns a Map<String, Map<String, Boolean>.

public class UserExtension
{
    private final User theUser;

    public UserExtension(ApexPages.StandardController stdController)
    {
        this.theUser = (User)stdController.getRecord();
    }

    public Map<String, Map<String, Boolean>> getMap()
    {
        Map<String, Map<String, Boolean>> theMap = new Map<String, Map<String, Boolean>>();
        theMap.put('Outer 1', new Map<String, Boolean> { 'Inner 1' => true, 'Inner 2' => false });
        theMap.put('Outer 2', new Map<String, Boolean> { 'Inner 1' => true, 'Inner 2' => true });
        return theMap;
    }
}

If I use this with an apex:repeat then everything works as expected:

<apex:page standardController="User" extensions="UserExtension">
    <apex:pageBlock >
        <apex:repeat value="{!Map}" var="outerKey">
            {!outerKey}
            <apex:repeat value="{!Map[outerKey]}" var="innerKey">
                {!innerKey}
                {!Map[outerKey][innerKey]}
            </apex:repeat>
        </apex:repeat>
    </apex:pageBlock>
</apex:page>

However, if I then try and switch the outer apex:repeat to an apex:pageBlockTable I get the following error on my page:

Map key null not found in map
Error is in expression ‘{!Map[outerKey]}’ in component in page userpage

<apex:page standardController="User" extensions="UserExtension">
    <apex:pageBlock >
        <apex:pageBlockTable value="{!Map}" var="outerKey">
            <apex:column value="{!outerKey}"/>
            <apex:repeat value="{!Map[outerKey]}" var="innerKey">
                <apex:column headerValue="{!innerKey}">
                    {!Map[outerKey][innerKey]}
                </apex:column>
            </apex:repeat>
        </apex:pageBlockTable>
    </apex:pageBlock>
</apex:page>

Is it possible to use nested maps with apex:pageBlockTable? Based on the behaviour of apex:repeat I would think it is. In that case, have I come across a bug or am I missing something really obvious?

Answer

Things often get a bit hairy when you start to use maps of maps (or mapception :D). You can represent tables of data in this way if you change your variable model.

Change the map of maps to be a list of a new inner class that contains simply the name of the inner property and a map of the outer properties to boolean values. Also add a method to retrieve a list of the column names, like so:

public class UserExtension
{
    private final User theUser;

    public UserExtension(ApexPages.StandardController stdController)
    {
        this.theUser = (User)stdController.getRecord();
    }

    public List<String> getColumns()
    {
        return new List<String>{'Outer1','Outer2'};
    }

    public List<innerClass> getMap()
    {    
        List<innerClass> theMap = new List<innerClass>{};
        theMap.add(new innerClass('Inner1', new Map<String, Boolean> { 'Outer1' => true, 'Outer2' => false}));
        theMap.add(new innerClass('Inner2', new Map<String, Boolean> { 'Outer1' => true, 'Outer2' => true }));
        return theMap;
    }

    public class innerClass
    {
        public String Name {get;set;}
        public Map<String, Boolean> outers {get;set;}

        public innerClass(String Name, Map<String, Boolean> outers)
        {
            this.Name = Name;
            this.outers = outers;
        }
    }
}

With this in place, then your page code changes to the following:

<apex:page standardController="User" extensions="UserExtension">
<apex:pageBlock >
    <apex:pageBlockTable value="{!Map}" var="innerKey">
        <apex:column value="{!innerKey.Name}"/>
        <apex:repeat value="{!columns}" var="col">
            <apex:column headerValue="{!col}">
                {!innerKey.outers[col]}
            </apex:column>
        </apex:repeat>
    </apex:pageBlockTable>
</apex:pageBlock>

Your table should now generate as required.

Attribution
Source : Link , Question Author : Alex Tennant , Answer Author : Alex Tennant

Leave a Comment