Roll Your Own Salesforce “Lookup” Popup Window

August 12th, 2011

Let’s talk about the standard salesforce.com “lookup” popup window for a few minutes. You know what I’m talking about.. this button right here

Custom lookup button

It’s a handy little button that pops up whenever you need to search for related records. It does a pretty good job but it has some serious drawbacks:

  1. It’s virtually impossible to modify your search criteria. What if you want your users to do some crazy search based upon custom logic or search a field that is not typically available? Sorry… you are out of luck. Not possible.
  2. It’s terrible for creating new records. Let’s say that a user searches for a specific related record and it (absolutely) doesn’t exist. To create the new record they need to close the lookup window, navigate to the tab to create the new related record, create the new record, then go back to the original record they were either editing or creating, pop up the lookup window again and find their newly created record. Wow! That’s a lot of work.
  3. “Quick Create” is evil! You can enable the “Quick Create” option for an entire org but don’t do it! It displays, by default, on the tab home pages for leads, accounts, contacts, forecasts, and opportunities! The major problems are that you can only create new records for these 5 objects (what about the other ones!?), you can’t customize the fields on the page and validation rules don’t fire (can you say, “bad data”).

Quick Create is Bad

Here’s the Solution!

I have some good news and some bad news. For standard page layouts I can’t help you. Go vote for this idea and this idea. However, for Visualforce page I have a solution to all of these problems with code!

Custom Lookup

Here’s how it looks. It may be easier to watch it full screen at YouTube.

Here’s the code you need to accomplish this. You need two Visualforce pages (the record you are editing and the popup window) and two Apex controllers (a simple one for the record you are editing and the controller for the search and new record popup).

MyCustomLookupControllercode at github

Here’s the Apex controller for the record you are either creating or editing. This is an extremely simple controller that just creates a new contact so you can use the lookup for the related account field.

1
2
3
4
5
6
7
8
9
public with sharing class MyCustomLookupController {
 
  public Contact contact {get;set;}
 
  public MyCustomLookupController() {
    contact = new Contact();
  }
 
}

MyCustomLookupcode at github

This is the “magical” Visualforce page that uses jQuery to intercept the popup and instead of showing the standard salesforce.com pop, shows our custom popup instead. The user experience is seamless.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<apex:page controller="MyCustomLookupController" id="Page" tabstyle="Contact">
 
  <script type="text/javascript"> 
  function openLookup(baseURL, width, modified, searchParam){
    var originalbaseURL = baseURL;
    var originalwidth = width;
    var originalmodified = modified;
    var originalsearchParam = searchParam;
 
    var lookupType = baseURL.substr(baseURL.length-3, 3);
    if (modified == '1') baseURL = baseURL + searchParam;
 
    var isCustomLookup = false;
 
    // Following "001" is the lookup type for Account object so change this as per your standard or custom object
    if(lookupType == "001"){
 
      var urlArr = baseURL.split("&");
      var txtId = '';
      if(urlArr.length > 2) {
        urlArr = urlArr[1].split('=');
        txtId = urlArr[1];
      }
 
      // Following is the url of Custom Lookup page. You need to change that accordingly
      baseURL = "/apex/CustomAccountLookup?txt=" + txtId;
 
      // Following is the id of apex:form control "myForm". You need to change that accordingly
      baseURL = baseURL + "&frm=" + escapeUTF("{!$Component.myForm}");
      if (modified == '1') {
        baseURL = baseURL + "&lksearch=" + searchParam;
      }
 
      // Following is the ID of inputField that is the lookup to be customized as custom lookup
      if(txtId.indexOf('Account') > -1 ){
        isCustomLookup = true;
      }
    }
 
 
    if(isCustomLookup == true){
      openPopup(baseURL, "lookup", 350, 480, "width="+width+",height=480,toolbar=no,status=no,directories=no,menubar=no,resizable=yes,scrollable=no", true);
    }
    else {
      if (modified == '1') originalbaseURL = originalbaseURL + originalsearchParam;
      openPopup(originalbaseURL, "lookup", 350, 480, "width="+originalwidth+",height=480,toolbar=no,status=no,directories=no,menubar=no,resizable=yes,scrollable=no", true);
    } 
  }
</script>
 
<apex:sectionHeader title="Demo"  subtitle="Custom Lookup" />
 
  <apex:form id="myForm">  
    <apex:PageBlock id="PageBlock">    
      <apex:pageBlockSection columns="1" title="Custom Lookup">
        <apex:inputField id="Account" value="{!contact.AccountId}"  />
      </apex:pageBlockSection>
    </apex:PageBlock>
  </apex:form>
 
</apex:page>

CustomAccountLookupControllercode at github

The Apex controller for the custom popup window is yours to customize. I know what you are thinking, “Free at last! Free at last! Thank God Almighty, we are free at last!” This class has all of your custom search functionality plus the method to create a new account. This is demo code so the search is very limited and does not prevent soql injections.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public with sharing class CustomAccountLookupController {
 
  public Account account {get;set;} // new account to create
  public List<Account> results{get;set;} // search results
  public string searchString{get;set;} // search keyword
 
  public CustomAccountLookupController() {
    account = new Account();
    // get the current search string
    searchString = System.currentPageReference().getParameters().get('lksrch');
    runSearch();  
  }
 
  // performs the keyword search
  public PageReference search() {
    runSearch();
    return null;
  }
 
  // prepare the query and issue the search command
  private void runSearch() {
    // TODO prepare query string for complex serarches & prevent injections
    results = performSearch(searchString);               
  } 
 
  // run the search and return the records found. 
  private List<Account> performSearch(string searchString) {
 
    String soql = 'select id, name from account';
    if(searchString != '' && searchString != null)
      soql = soql +  ' where name LIKE \'%' + searchString +'%\'';
    soql = soql + ' limit 25';
    System.debug(soql);
    return database.query(soql); 
 
  }
 
  // save the new account record
  public PageReference saveAccount() {
    insert account;
    // reset the account
    account = new Account();
    return null;
  }
 
  // used by the visualforce page to send the link to the right dom element
  public string getFormTag() {
    return System.currentPageReference().getParameters().get('frm');
  }
 
  // used by the visualforce page to send the link to the right dom element for the text box
  public string getTextBox() {
    return System.currentPageReference().getParameters().get('txt');
  }
 
}

CustomAccountLookupcode at github

Any finally the Visualforce page for the popup itself. It contains a tabbed interface easily allowing a user to search for records and create new ones. Make sure you look at the code for the second tab for creating a new record. I have better things to do than change the fields on the input form every time a new field is created or something is made required. The solution is to use field sets! So when an administrator makes a change, they can simply update the field set and the popup reflects the change accordingly. Life is good.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<apex:page controller="CustomAccountLookupController"
  title="Search" 
  showHeader="false" 
  sideBar="false" 
  tabStyle="Account" 
  id="pg">
 
  <apex:form >
  <apex:outputPanel id="page" layout="block" style="margin:5px;padding:10px;padding-top:2px;">
    <apex:tabPanel switchType="client" selectedTab="name1" id="tabbedPanel">
 
      <!-- SEARCH TAB -->
      <apex:tab label="Search" name="tab1" id="tabOne">
 
        <apex:actionRegion >  
          <apex:outputPanel id="top" layout="block" style="margin:5px;padding:10px;padding-top:2px;">
            <apex:outputLabel value="Search" style="font-weight:Bold;padding-right:10px;" for="txtSearch"/>
            <apex:inputText id="txtSearch" value="{!searchString}" />
              <span style="padding-left:5px"><apex:commandButton id="btnGo" value="Go" action="{!Search}" rerender="searchResults"></apex:commandButton></span>
          </apex:outputPanel>
 
          <apex:outputPanel id="pnlSearchResults" style="margin:10px;height:350px;overflow-Y:auto;" layout="block">
            <apex:pageBlock id="searchResults"> 
              <apex:pageBlockTable value="{!results}" var="a" id="tblResults">
                <apex:column >
                  <apex:facet name="header">
                    <apex:outputPanel >Name</apex:outputPanel>
                  </apex:facet>
                   <apex:outputLink value="javascript:top.window.opener.lookupPick2('{!FormTag}','{!TextBox}_lkid','{!TextBox}','{!a.Id}','{!a.Name}', false)" rendered="{!NOT(ISNULL(a.Id))}">{!a.Name}</apex:outputLink>     
                </apex:column>
              </apex:pageBlockTable>
            </apex:pageBlock>
          </apex:outputPanel>
        </apex:actionRegion>
 
      </apex:tab>
 
      <!-- NEW ACCOUNT TAB -->
      <apex:tab label="New Account" name="tab2" id="tabTwo">
 
        <apex:pageBlock id="newAccount" title="New Account" >
 
          <apex:pageBlockButtons >
            <apex:commandButton action="{!saveAccount}" value="Save"/>
          </apex:pageBlockButtons>
          <apex:pageMessages />
 
          <apex:pageBlockSection columns="2">
            <apex:repeat value="{!$ObjectType.Account.FieldSets.CustomAccountLookup}" var="f">
              <apex:inputField value="{!Account[f]}"/>
            </apex:repeat>
          </apex:pageBlockSection> 
        </apex:pageBlock>
 
      </apex:tab>
    </apex:tabPanel>
  </apex:outputPanel>
  </apex:form>
</apex:page>

VN:F [1.9.15_1155]
Rating: 7.5/10 (2 votes cast)
VN:F [1.9.15_1155]
Rating: +3 (from 3 votes)
Roll Your Own Salesforce "Lookup" Popup Window, 7.5 out of 10 based on 2 ratings

Categories: Apex, Salesforce, Visualforce

Leave a comment

Comments Feed17 Comments

  1. imfromthepast

    Oh. My. God.
    Believe it or not, I began today preparing to try to come up with a way to override the standard lookup window. I had no idea where to start, so I decided to put it off. In my procrastination I checked my Google Reader, and there, in all its glory, was this post outlining the exact solution I needed!
    Thank you Jeff for your near-divine timing, you saved me a ton of work, and your solution works perfectly for me, with minimal changes (thanks for the comments too, they were a big help in changing your example from an account lookup to a contact lookup).
    Thank you so much!!

    VA:F [1.9.15_1155]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.15_1155]
    Rating: 0 (from 0 votes)
  2. Jeff Douglas

    That’s awesome Jim! Glad I could help out.

    VN:F [1.9.15_1155]
    Rating: 0.0/5 (0 votes cast)
    VN:F [1.9.15_1155]
    Rating: 0 (from 0 votes)
  3. Richard Tuttle

    Ironically I was just complaining about this missing feature from the standard layouts the other day. I really with SFDC would adopt this into standard.

    Great article! If I ever get around to converting the offending app I’ll have to drop this in to make life easier.

    VA:F [1.9.15_1155]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.15_1155]
    Rating: 0 (from 0 votes)
  4. Jose

    Hi i’m trying to use your solution but have one question, teh function lookupPick2? what does it do? or where can i find it?

    VA:F [1.9.15_1155]
    Rating: 5.0/5 (1 vote cast)
    VA:F [1.9.15_1155]
    Rating: 0 (from 0 votes)
  5. Jeff Douglas

    Thant actually opens the record found from the search in the opening page.

    VN:F [1.9.15_1155]
    Rating: 0.0/5 (0 votes cast)
    VN:F [1.9.15_1155]
    Rating: 0 (from 0 votes)
  6. Michał Szewczyk

    Hi Jeff,
    Nice article , sometime ago I found the way to use custom lookup button also on standard page layouts.
    All you have to do is to ,put some java script in your page ( section header ) which will override standard link. Works and it’s a nice workaround till SF provide it in standard.

    VA:F [1.9.15_1155]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.15_1155]
    Rating: 0 (from 0 votes)
  7. Tal

    Hi Jeff,
    Thanks for this awesome article. Just one question: what is calling the function “openLookup”? How does it “catch” the popup button click?
    Thanks,
    Tal

    VA:F [1.9.15_1155]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.15_1155]
    Rating: 0 (from 0 votes)
  8. Gerard

    This is a real nice improvement, thanks!

    For those who have another lookup field on the new record tab, the default action is to reuse the window on the inner lookup.
    Change the name of the lookup to something else, like below and you will get both lookups working.

    MyCustomLookup.page
    if(isCustomLookup == true){
    openPopup(baseURL, “lookup1″, 350, 480,….
    }
    else {

    But the inner lookup box wont die if you click off it (normal behaviour). if you cant live with this then you will have to dig into Salesforce’s main.js and override some other functions there. I tried but I ended up going backwards fast so i left it.

    Also, the lookuptype variable still in MyCustomLookup.page wasnt working with the given code. I think sfdc was giving me a different baseURL so i used the following code to rememdy that.

    var lookupType = baseURL.substr(baseURL.indexOf(‘lktp’)+5, 3);

    Keep in mind that this is not deployable in a package for custom objects because the lookuptype will change for each installation.
    the describe methods could help out there.

    Cheers, Jeff. I love reading you blog!

    VA:F [1.9.15_1155]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.15_1155]
    Rating: 0 (from 0 votes)
  9. Gerard

    Just a follow-up on my previous post.
    To make the page deployable with the describe methods, you can use in the MyCustomLookupController class the following code.

    // a property for the lookuptype
    public String GetKeyPrefix {get;private set;}

    public ScenarioEdit(ApexPages.StandardController controller) {
    GetKeyPrefix = MyCustomObject__c.SObjectType.getDescribe().getKeyPrefix();

    }

    and in the page

    if(lookupType == “{!GetKeyPrefix}”){

    VA:F [1.9.15_1155]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.15_1155]
    Rating: 0 (from 0 votes)
  10. Siva

    Thanks Jeff… I would like to pass a value from the main controller to second controller. For Example : in the main controller I have two lookup values , i just want to get it in second controller because based on these fields i will be doing search functionality. Any idea ?

    VA:F [1.9.15_1155]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.15_1155]
    Rating: 0 (from 0 votes)
  11. Anand

    Dear Jeff,
    Thanks for the detailed article, will it be similarly applicable for external sites?
    I tried – the look up filed is not appearing on the page. Please advice.

    Thanks,
    Anand

    VA:F [1.9.15_1155]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.15_1155]
    Rating: 0 (from 0 votes)
  12. Jeff Douglas

    @Anand, it’s probably because the Sites user does not have access to that field. You can configure access for that field in the Site.

    VN:F [1.9.15_1155]
    Rating: 0.0/5 (0 votes cast)
    VN:F [1.9.15_1155]
    Rating: 0 (from 0 votes)
  13. BruceYue

    nice

    VA:F [1.9.15_1155]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.15_1155]
    Rating: 0 (from 0 votes)
  14. mayank

    Hello Jeff ,

    I am getting an error ,Plz sort it out

    I just want to show and hide the output panel on changes the value of dropdown box

    Capgemini Travel Desk

    function changevalue(txt)
    {

    if(txt.value==”Flight (International)”)
    //alert(document.getElementById(“flight”));
    alert(txt.value);
    if(txt.value == ‘Flight’)
    {
    document.getElementById(‘{!$Component.flight}’).style.visibility = ‘visible’;

    }

    }

    

    but this is not working .Moreover plz tell me how to do the same with the help of action function .

    Thanks

    VA:F [1.9.15_1155]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.15_1155]
    Rating: 0 (from 0 votes)
  15. Patrick Dixon

    Just to say thanks for this – it’s brilliant!

    Tweeked it a little to pick up a hidden text field on my vf page to pass to the lookup page/controller – so that we know whether we’re looking-up Accounts or PersonAccounts.

    VA:F [1.9.15_1155]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.15_1155]
    Rating: 0 (from 0 votes)
  16. Dmitri Sennikov

    Hi Jeff,

    With your help I’ve added the openLookup function override as custom HTML component into my sidebar (have to set “show custom sidebar components on all pages”). Now I don’t need to create custom page, any standard page has custom look up!

    Thanks,
    Dmitri

    VA:F [1.9.15_1155]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.15_1155]
    Rating: 0 (from 0 votes)
  17. John

    Hi Jeff,

    Great stuff! Would you be able to use the same concept if you had a bunch of lookup fields on the same VF page.

    Example, I’ve got a bunch of Contact lookup fields in a table format, and I’d like to override the lookup for all the Contact lookups on my page.

    Also, I’m not quite sure from looking at the code how the baseURL comes into play? Is that a variable that you’re passing in from somewhere else?

    Thanks!

    VA:F [1.9.15_1155]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.15_1155]
    Rating: 0 (from 0 votes)

Leave a comment

Feed

http://blog.jeffdouglas.com / Roll Your Own Salesforce “Lookup” Popup Window

WordPress Appliance - Powered by TurnKey Linux