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.

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.

share|improve this question
    
Great! Half the battle with coding Apex is figuring out the workaround for stuff that seems like it should work but just doesn't. –  Peter Knolle Sep 20 '12 at 18:04
add comment

4 Answers

up vote 4 down vote accepted

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

share|improve this answer
    
Key word is "should" :) The VF complained that there was no 'Id' in the ListWrapper, and when I added a token attribute id complained there was no 'Name'. Makes me think that only Lists of Sobjects work and a wrapper is not possible. –  George S. Sep 19 '12 at 12:45
    
@user449 - I updated my code sample and I was able to get the size to output in my VF. I think that the error that you are getting could be due to the VF needing updating. Can you double check your VF to make sure that after changing to use the wrapper that all of your references access the wrapper where appropriate (e.g., wrapper[a].theList and not wrapper[a].Id, or however you have it wrapped. –  Peter Knolle Sep 19 '12 at 13:00
    
Please see my third edit above. I really appreciate your time. –  George S. Sep 19 '12 at 13:24
1  
The error above was in a different section of the VF. The code is now working. Thanks so much. –  George S. Sep 19 '12 at 13:42
    
I just ran into something similar enough to add a note. If you have a map with valid keys pointing to null values (e.g. thisMap['validKey'] = null, this will ALSO trigger an error in Visualforce. You can't use formulas to test whether the value is null. (This is documented, but easy to miss.) –  Benj Sep 11 '13 at 21:40
add comment

you can do the one below : public Map> accountsMap {get; set;}

Refer : http://bobbuzzard.blogspot.com/2011/03/visualforce-dynamic-map-bindings.html

LMK if this helped :)

share|improve this answer
    
See my edit above. I'm doing something similar to the link, but can't quite get it right. –  George S. Sep 18 '12 at 20:47
add comment

Cleaner version :

<apex:page controller="testconr" > 
<apex:form > 
<apex:repeat value="{!acc}" var="a"> 
<apex:outputLink value="{!a}" rendered="{!If(ISBLANK(cons[0]),true,false)}"> hai </apex:outputLink>
</apex:repeat> 
</apex:form>
</apex:page>

controller :

public with sharing class testconr {
public map<integer,string>cons{get;set;}
List<Account> acc = [select id,name from account limit 10];

public list<account> getAcc() {
cons = new map<integer,string>();
system.debug('ACC VALUE IS' + acc);
for( integer i=0;i<acc.size();i++){
cons.put(i,acc[i].name);
}
    return acc;
}

}
share|improve this answer
    
Use rendered="{!If(NOT(ISBLANK(cons[0])),true,false)}"> to check for not null –  rao Sep 18 '12 at 21:28
add comment

You're pretty close. In your first example at least, you had a few syntax type mistakes.

Example: rendered="{!IF({!cons}.get({!a}) == null, true, false)}" You have extra curly braces and exclamation marks around cons and a. Also, you cannot access the get method like that. In visualforce, you simply surround the key with square brackets like "cons[a]"

Also, rao is close as well, but he's only looking at the 0th element in the list instead of all the keys.

I've implemented a similar rolodex before, think it was using very similar if not nearly identical logic.

<apex:repeat var="letter" value="{!alphabetList}">
  <apex:outputLink value="{!letter}" rendered="{!IF(NOT(ISBLANK(cons[letter])))}">{!letter}</apex:outputLink>
  <apex:outputText rendered="{!IF(ISBLANK(cons[letter]))}">{!letter}</apex:outputText>
  &nbsp;|&nbsp;
</apex:repeat>

The above Visualforce should output a link if a letter has records associated with it, else it will output the character without a link. You can also substitute commandLink for outputLink and link back to an action in your controller. Another option as well is to use the same link for all letters in the alphabet, but then use the apex:param child element under apex:outputLink to pass the letter of the alphabet with the link if that makes sense.

share|improve this answer
add comment

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.