[SalesForce] Can’t get “rendered” to work with checking a map for null

I have been trying for a good few hours to only get my apex:outputLink to show up if it is a key within a map in my controller.

It keeps giving me the generic 'Syntax Error'.

Here's the VF:

<apex:repeat value="{!alphabet}" var="a">
        <apex:outputLink value="{!a}" rendered="{!IF({!cons}.get({!a}) == null, true, false)}">{!a}</apex:outputLink>                       
        &nbsp;|&nbsp;
</apex:repeat>

So "cons" is a Map>() that gets loaded in the constructor. "alphabet" is a simple List of Strings – "A", "B", "C" all the way to "Z".

Basically I would like to know if cons.get("A") == null, cons.get("B") == null, and only show the outputLink if this is not the case.

EDIT: I've tried with:

rendered="{!IF(!cons[a] != null, true, false)}"

which at least compiles, but gives me the error "Map key A not found in map"

EDIT2: I've made sure cons contains all the keys. So now I'm looking to see if the associated List size is greater than 0. However

<apex:outputLink value="{!a}" rendered="{!IF(cons[a].size!=0, true, false)}">{!a}</apex:outputLink>

gives the following: Incorrect parameter for subscript. Expected Number, received Text

EDIT3 – UPDATE FOR PETER

Controller:

public Map<String, ContactListViewController.ListWrapper> cons {get;set;}

private class ListWrapper{

    public List<Contact> theList {get;set;}

    public ListWrapper(){}

    public ListWrapper(List<Contact> theList){

        this.theList = theList; 
    }

    public Integer size {

        get {
            return theList.size();
        }
    }
}

VF:

<apex:repeat value="{!alphabet}" var="a">
                <apex:outputLink value="{!a}" rendered="{!IF(cons[a].size>0, true, false)}">{!a}</apex:outputLink>                      
                &nbsp;|&nbsp;
</apex:repeat>

This code is working fine.

Best Answer

It is not possible to check whether a Map in your controller has a key in it from your VF page in a dynamic fashion the way that you were trying. From your VF page, if you try to access a Map with a key that does not map to a value you will get an error. See the VF developers guide section on Lists and Maps for more information.

I see that you fixed that in EDIT 2, by making sure that all keys are in cons, so that solves that part.

Now you are getting the error: Incorrect parameter for subscript. Expected Number, received Text

<apex:outputLink value="{!a}" rendered="{!IF(cons[a].size!=0, true, false)}">{!a}</apex:outputLink>

I reproduced this error. I can access cons[a][0] and get the 0th element of each List in each Map entry and I can also just write out cons[a] without an issue. It is just an issue when trying to access size. I'm not totally sure why.

To get around your problem you could add an extra layer of indirection to wrap the list and get the size differently: Update: put the code inside of a class

public class ListWrapperController {
    public class ListWrapper {
        public ListWrapper(List<String> t) {
            theList = t;
        }
        public List<String> theList { get; set; }
        public Integer size { 
            get { 
                return theList.size(); 
            }
        }
    }

    public List<String> alphabet { 
        get { 
            return new List<String>{'a','b','c','d','e'}; 
        }
    }
    public Map<String, ListWrapper> cons { 
        get { 
            return new Map<String, ListWrapper>{
                'a'=>new ListWrapper(new List<String>()), 
                'b'=>new ListWrapper(new List<String>{'bbbb'}), 
                'c'=>new ListWrapper(new List<String>{'cccc'}), 
                'd'=>new ListWrapper(new List<String>{'dddd'}), 
                'e'=>new ListWrapper(new List<String>{'eeee'})
            }; 
        } 
    }
}

Then in the VF your call to size should work because it will be calling your wrapper class's size method (or whatever you name the method...doesn't have to be size) and not the actual Apex List's size method.

<apex:repeat value="{!alphabet}" var="a">
    <apex:outputLink value="{!a}" rendered="{!cons[a].size > 0}" >{!a}   </apex:outputLink>
</apex:repeat>

I tested that controller and that VF snippet and it ouptputted the following links (note no 'a' link since the ListWrapper that 'a' maps to has an empty list):

b c d e