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
Wow, I don't know how to feel about this. The answer is, Don't use apex:inputCheckbox, always use apex:inputField when binding to a value on a controller. Apex will do the work of knowing it's a boolean and making it a checkbox.
Ouch, that was a bunch of time wasted.
Maybe someone could add this to the apex:inputCheckbox documentation, right after it says this: "Use this component to get user input for a controller method that does not correspond to a field on a Salesforce object." - to say, "If you're binding a checkbox to a controller, use apex:inputfield" and it will be rendered as a checkbox.
Best Answer
Looks like you have a few extra exclamations/bangs (
!
) in your markup there.In Visualforce, when you start using merge field syntax, it requires a bang. You don't need to always put a bang before referencing a field. Anything after the first bang acts as a logical NOT, which is where I think you're running into problems.
Your current markup,
IF(!selectedPackage <> null...
is equivalent toIF(NOT(selectedPackage) <> null...
.NOT()
and!
only work on boolean fields, and it appears thatselectedPackage
is a text field rather than a boolean/checbox.Instead of checking for nulls manually, I'd probably recommend using
ISBLANK()
.This should work: