15

is it possible to check for a specific key in a map i.e., whether it exists or not in VISUALFORCE page?

Based on this i need to render a pageblocktable.

  • Any reason why can't do this in controller? – Sergej Utko Aug 21 '13 at 13:54
  • actually my pageblocktable will be in apex:repeat and each time keyset of map would be different(I have other conditions in controller which populate the map with varying no.of keys). so i cant do this in controller – rani Aug 21 '13 at 14:04
  • See my answer on how I resolved this problem. – sfdcfox Aug 21 '13 at 14:12
10

No, there's no way to tell if a key exists or not. We don't have access to "containsKey", and any attempt to reference a key that doesn't exist will cause your code to crash and burn catastrophically.

Visualforce Error
Help for this Page

Map key 'value' not found in map
Error is in expression '{!not(isnull(keys[key]))}' in component <something> in page xyz

This is related to bug W-1065879 (according to an old forum post).

The solution is to check the key in Apex Code, and prevent the rendering of the element or component before the attempted access.

Here's an example used in live code:

public with sharing class orderCreateController {

    public Map<Id,Boolean> selectId { get; set; }
    public Map<Id,Quote> quoteList  { get; set; }

    // Irrelevant code redacted here

    // FIX FOR BUG W-1065879
    public wrapper[] getwrapperlist() {
        wrapper[] items = new wrapper[0];
        if(quotelist != null) {
            for(id quoteid:quotelist.keyset()) {
                items.add(new wrapper(this,quoteid));
            }
        }
        return items;
    }

    public class wrapper {
        public wrapper(orderCreateController c,id recordid) {
            con = c;
            id = recordid;
        }
        public boolean selected { get { return con.selectid.get(id); } set { con.selectid.put(id,value); } }
        public quote quote { get { return con.quotelist.get(id); } }

        private id id;
        private orderCreateController con;
    }
}

By iterating over {!wrapperlist}, we can create a pseudo-wrapper that will prevent non-existent keys from appearing.


EDIT

I just realized that this code takes some explaining. You can select from a list of quotes. The map may not always be populated with boolean values, so this wrapper class protects the Visualforce map controller from croaking.

6

You should really keep this type of logic out of a Visualforce page. A Visualforce page should be used simply for View logic. Instead, add it as part of your controller:

Apex

public Boolean getRenderPageBlockTable(){
    return mapToCheck.containsKey('keyToCheck');
}

Visualforce

<apex:pageBlockSection rendered="{!renderPageBlockTable}">
    ...
</apex:pageBlockSection>
  • But if that pageBlockSection is inside an apex:repeat, with a different 'keyToCheck' on each iteration, you have no way to pass the key back to the getRenderPageBlockTable to it can be checked. Herein lies the problem! – cyberspy 8 hours ago
3

You can build a delimited string with all the keys of the map in controller and then you use SF formula

CONTAINS(delimited_string_with_map_keys, key)

in a rendered condition display map values with the key.

3

Actually, you can build that natively on Visualforce Page, without any further variables. You need to use an apex:repeat, iterate over it, and inside the apex:repeat you may use an outputText (or similar and render the outputText just in case the iterated value matches your expected value. For example:

<apex:repeat value="{!yourMapVariable}" var="key"> <!--yourMapVariable is a map and you are not sure whether it contains or not the key 'Name' -->
     <apex:outputText rendered="{!key=='Name'}"> <!-- kind of containsKey(...) -->
         {!yourMapVariable[key]} <!-- this is now safe and will never produces an exception -->
     </apex:outputText>
</apex:repeat>
  • I would not recommend this approach. While it would work, it means you need to iterate over every single value in your list to find it. I would guess that this would be much slower to do than just running a .containsKey in Apex and returning that to your page through a controller. – Jesse Altman Feb 28 '14 at 14:23
  • the containsKey in Apex "iterates" also in order to find whether the key exists in the Map. I understand it's obviously an hack, but writing extra logic in Apex is not better either – Christophe Vidal Mar 3 '14 at 13:59
  • Also it doesn't perform "much slower". Actually, I have never noticed any difference at all, but that might be kept in mind in case the pages becomes slow to load - or the collection is too huge for an apex:repeat (limit is 1000 AFAIK) – Christophe Vidal Mar 3 '14 at 14:06
  • 1
    Unless SF has implemented something other than a traditional map, containsKey DOES NOT iterate each item! It generates a Hash Code to determine the memory offset of the item. You solution would be O^n while @JesseAltman solution is typically O(1). Data-Structures 101. – NSjonas May 24 '16 at 7:52
0

Instead of checking in VF page, I check in the code itself when you are populating the List/Map. I'm also using an of list items to display

personReferList is full list of apex:repeat.

   //creating a new List to hold the records which exists in map
   List<Person__c> PIList = new List<Person__c>();  
      //looping the list of records displayed in VF page                                
      for(Person__c pi: personReferList){
           //checking if all the records are in the Map
           if(mapPersonRef.containsKey(pi.Id)){
               //If yes, adding it to a new list. 
               PIList.add(pi);
           }
       }
  //clearing the original list first
  personReferList.clear();
  *//adding the from original list to the original list*
  personReferList.addAll(PIList);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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