Saturday, 25 September 2010

VisualForce Lookup

As VisualForce developers know, adding an inputfield backed by a lookup or master detail field renders a field to enter a text string into and a magnifying glass icon for launching the Salesforce lookup popup. There isn't much customization possible for the lookup popup, so often there is a need to replace it with one matching your own requirement.

This blog entry shows how to create your own visualforce lookup page backed by a custom controller that gives you full control.

My basic page for demonstrating custom lookup functionality is shown below:


The Lookup link pops up the lookup window.  Search text is entered into the input field and clicking the Go button returns the results into the results section:



Clicking the name populates the lookup text field on the main page, and to prove that it is working, clicking the Get Contacts button retrieves the contacts associated with the account:




The first point to mention regarding lookups is that there are actually two fields that are filled in on the target page when you select a lookup result.  The name of the selected item is displayed in the visible field, but names are not unique so this is not enough information to uniquely identify the chosen item.  Thus there is a hidden field that captures the the ID of the selected record.

Below is a snippet of a VisualForce page that contains my custom lookup combination field.  For the sake of simplicity I've chosen to use a clickable link to launch the lookup dialog.

<apex:pageBlock title="Lookup">
      <apex:pageBlockSection columns="1">
        <apex:pageBlockSectionitem >
          <apex:outputLabel value="Account"/>
          <apex:outputPanel >
         <apex:inputHidden value="{!accountId}" id="targetId" />
            <apex:inputText size="40" value="{!accountName}" id="targetName" onFocus="this.blur()" disabled="false"/> <a href="#" onclick="openLookupPopup('{!$Component.targetName}', '{!$Component.targetId}'); return false">Lookup</a>
          </apex:outputPanel>
        </apex:pageBlockSectionitem>
      </apex:pageBlockSection>
      <apex:pageBlockSection >
        <apex:pageBlockSectionitem >
           <apex:commandButton value="Get Contacts" action="{!findContacts}"/>
        </apex:pageBlockSectionitem>
      </apex:pageBlockSection>
    </apex:pageBlock>

Digging into the markup, we can see that as well as the text field and link, the hidden field to capture the link is also present.  When the user clicks the link, the following javascript is executed to open the popup.  Note that the HTML element ids are passed as parameters to the popup page - this allows the popup to locate the fields to populate when the user makes a selection.  Note also that the javascript function to close the popup window is located in the main page, not the popup page.  This is required as browsers often only allow a window to be closed by the same page that opened it.

<script>
 var newWin=null;
 function openLookupPopup(name, id)
 {
  var url="/apex/LookupExamplePopup?namefield=" + name + "&idfield=" + id;
  newWin=window.open(url, 'Popup','height=500,width=600,left=100,top=100,resizable=no,scrollbars=yes,toolbar=no,status=no');
  if (window.focus) 
  {
   newWin.focus();
  }
   
     return false;
    }
      
 function closeLookupPopup()
 {
    if (null!=newWin)
    {
       newWin.close();
    }  
 }
</script>


Now we move on to the popup VisualForce page. This consists of a criteria section and a results table.  Clicking the Go button invokes a controller action method that executes a SOSL query to retrieve all accounts matching the input string. The VisualForce markup for the results table is shown below.

<apex:pageBlock >
          <apex:pageBlockSection columns="1">
              <apex:pageBlockTable value="{!accounts}" var="account">
                <apex:column headerValue="Name">
                  <apex:outputLink value="#" onclick="fillIn('{!account.Name}', '{!account.id}')">{!account.Name}</apex:outputLink>       
                </apex:column>
                <apex:column headerValue="Street" value="{!account.BillingStreet}"/>
                <apex:column headerValue="City" value="{!account.BillingCity}"/>
                <apex:column headerValue="Postcode" value="{!account.BillingPostalCode}"/>
              </apex:pageBlockTable>    
          </apex:pageBlockSection>
        </apex:pageBlock>

As you can see, the account name column is actually a link that invokes a JavaScript function shown below:

function fillIn(name, id)
   {
      var winMain=window.opener;
      if (null==winMain)
      {
         winMain=window.parent.opener;
      }
      var ele=winMain.document.getElementById('{!$CurrentPage.parameters.namefield}');
      ele.value=name;
      ele=winMain.document.getElementById('{!$CurrentPage.parameters.idfield}');
      ele.value=id;
      CloseWindow();
   }

   function CloseWindow()
   {
      var winMain=window.opener;
      if (null==winMain)
      {
         winMain=window.parent.opener;
      }
      winMain.closeLookupPopup();
   }

This JavaScript is the glue between the two pages - it fills in the HTML elements in the main page and closes the popup window.

The pages and controllers can be downloaded hereSimply unzip into the src directory of your Force.com project in the IDE.

56 comments:

  1. Nice effort, I would suggest using HTML z-index to overlay a div, like jquery colorbox etc to achieve this.

    ReplyDelete
  2. Nice, thanks for the information. I am new to the technology searching for what can do & how can do with components of visualforce.

    ReplyDelete
  3. @Abhinav - yes, layering is the next logical step. It would remove some javascript for focus grabbing and window closing. As always, time is the limiting factor ;)

    ReplyDelete
  4. Can you use something like this on a sites page and have a lookup filter? The application is this: An user (role = product distributor) logs into a VF sites page, they are registering a deal (which goes into SFDC as a lead) and need to connect a partner account, which is a reseller that uses that distributor, (which is a lookup field on the lead) to the lead. Basically, the distributor is registering the deal on behalf of the reseller.

    I want the distributor to only have access to look up reseller accounts that are linked to his account.

    Would this solution be a starting point for that type of solution?

    Thanks
    joannculbertson@gmail.com

    ReplyDelete
  5. @JoAnn - I guess it could be used as a starting point for that. The controller would need some work to pull back the logged in user's information and apply a filter to the query. That said, it looks more like a partner portal use case to me - is there any reason why you wouldn't go that route?

    ReplyDelete
  6. Thanks for the post and source!

    This is a nice little example that can be extended. I am using it as a simple starting point and extending it and its working nicely.

    I like that it creates a popup similar to standard SF Lookup functionality. Its transparent to user that this is VF page as it behave just like SF lookup on standard edit pages.

    Thanks!

    ReplyDelete
  7. Bob,

    Im trying your code in eclipse force IDE. It works on its own, but when I try to integrate it into my own visualforce page, I get this error:

    "Description Resource Path Location Type
    Save error: Unknown constructor 'LookupMainController.LookupMainController(ClaimAndRequest controller)' ClaimAndRequest.page /AtsMemberUpdate/src/pages line 0 Force.com save problem
    "


    Any idea what this means?

    ReplyDelete
  8. This usually means that you have declared LookupMainController as an extension to to the standard controller for ClaimAndRequest objects. An extension controller must provide a constructor that takes the standard controller as a parameter.

    ReplyDelete
  9. A nice solution in keeping with Salesforce behaviour. If only Salesforce would allow you to pass filter arguments to lookups ...

    ReplyDelete
  10. Hi Bob:

    Nice example. In your LookupExampleMain.page, this is a custom Visualforce page. If you wanted to "glue" the custom loookup and populate a field on a "stock" salesforce page, would your example work in this case?

    Any hints, pointers here in achieving this?

    thx.
    W. MacKenzie

    ReplyDelete
  11. Unfortunately this only works for a custom page, as the standard lookup icon doesn't allow you to control what happens when it is clicked.

    ReplyDelete
    Replies
    1. Understood. I created a customer VF page to try it out using your example. I hooked it up to invoke my VF page controller/page setup to perform the lookup.

      It doesn't appear however to be passing the args (targetName and targetId) to the VisualForce page.

      What should the url look like that is being assembled?

      Delete
    2. got it. I was attempting to return values in the fillin method with values which were not yet completed. I was expecting the action method on the inputText to fire before onclick but it wasn't. Replaced onclick with oncomplete and it fixed it.

      Delete
  12. i used this logic in pageblock table column and its working fine. The requirement is to open the popup inline..I used the " Css display :inline" but what is happening is when i click on any of the values in column it opens the pop up inline to the first row only.. Could you please help me out. Its on an urgent basis..

    I would be good if u give me ur mail id.. I will share my code with u

    ReplyDelete
    Replies
    1. I can't provid 1-1 help I'm afraid. Best I can suggest is to post your code up to the developerforce visualforce discussion board.

      Delete
  13. I want to add 'X' next to the lookup icon which clears the input field value. After the field gets populated, later when I open the visualforce page, the field is prepopulated with that value. When I click X, the input field should be nulled. How is it possible?

    ReplyDelete
  14. Wow....its very similar to standard functionality lookup of salesforce .

    ReplyDelete
  15. Hi bob,
    If there is possible to call the visualfore page from the default layout of th object. i.e my requirement is i want to call the poopup page from the standard page lookup field instead of main page.

    ReplyDelete
    Replies
    1. My requirement is simular. They want to replace the lookup field with a visualforce page. I have built the visulforce page and got it to search. But now am stuck on replacing the field. I am rebuilding the standard page into a visual force page to do this. I am trying to use an extension for my popup controller but am lost. Such a newbie it is sad..

      Delete
  16. this is an original article by Jeff Douglas... please provide appropriate credits...

    ReplyDelete
    Replies
    1. Except that Jeff wrote his post on August 12th 2011, while I wrote mine on 25th September 2010 - quite impressive of me to use his post nearly a year before he'd written it.

      Believe it or not, its possible to two people to write a blog post on the same topic independently.

      Delete
    2. Yes Bob ! I did realize that later and your 100% right about ur work being original. Also its possible for two people to write a blog post the same but the reason I pointed out was the code snippet was exactly the same :-) I will right away post on Jeff's blog to provide you the credits !

      Delete
  17. Yes Bob ! I did realize that later and your 100% right about ur work being original. Also its possible for two people to write a blog post the same but the reason I pointed out was the code snippet was exactly the same :-) I will right away post on Jeff's blog to provide you the credits !

    ReplyDelete
  18. Works perfectly, Thanks! Combined with <img src="/s.gif" alt="Location Name Lookup (New Window)" .... to give me the hourglass icon

    ReplyDelete
  19. when i try to use it gives an error as accountid is not a property

    ReplyDelete
    Replies
    1. Accountid is a property in the main lookup controller that is downloaded from the link - have you downloaded all of the source?

      Delete
  20. Hi Bob, I've been trying to figure out how to pass the value of the input text name to the OpenLookupPopup with no luck. It works great without it, but I know my users will attempt to enter text then click the search button. Seems the value of the text is not set at the time the pop up is called. Your wizardry is much appreciated. Thanks!

    ReplyDelete
    Replies
    1. You'll need to pull the value of the underlying HTML element and pass that through as an additional parameter to the popup URL, then get the popup controller to execute a search if the parameter has a value.

      Delete
  21. Thank you for your post. I used some of this code to help me build something slightly different. I got the visual page to run. Yeah!

    ReplyDelete
  22. Thank you so much for your post! It's very useful!

    ReplyDelete
  23. Hi Bob,
    Your post is quite impressive. I have a li'l similar requirement to this. In a visualforce page I have to display Account lookup. But when user don't find the record he is looking for, I need to capture the text entered by him in that field and create an Account from the controller. Is it possible to click a button by just entering a text in the lookup field even if no record exist matching with the text entered? Please guide me. TIA.

    ReplyDelete
  24. Hi Bob,

    Thank You for the Post. I have similar requirement where I have 2 lookups and based on first lookup value i need to go and retrieve the 2nd lookup values. Please let me know your thoughts how to achieve it.



    Thank You

    ReplyDelete
  25. This one really helped me a lot one year ago... :) Nice post bingo..!!!!!

    ReplyDelete
  26. Hi Bob,
    Thank you for the Post. Really helped me a lot.

    But currently i am facing an issue with look-up. The look-up data is getting cleared in page where as the data is getting successfully saved in database. Can you please help me out.

    Thank You.

    ReplyDelete
  27. Thanks for the Information Bob.....
    It helped to complete my task....

    ReplyDelete
  28. Hello Bob,

    Once account name is populated in main page, I want to go and fetch some more fields from account object and show them in the same age. I tried action support , but it is not firing onchange event. do you have any suggestion.


    ReplyDelete
  29. @Bob, is there a way to tie up the lookup with inputText directly. I don't use standard ID field to search records but will use the custom 'text' input.

    ReplyDelete
  30. Hi Bob,

    This page is not working for me in public sites. When click on lookup link the popup is coming but page is showing under construction. I have given page,class and obj access. please let me know how to solve this issue.

    Thanks,
    Lakshmi

    ReplyDelete
  31. Hello,
    It worked fine, but I have a little problem.
    When I check the value that is being sent in: !$Component.targetName} I don't get a value but something like a reference j_id0:j_id2:targetName.

    Do you know what can I be doing wrong?
    Thanks!

    ReplyDelete
  32. Do you have test class?

    ReplyDelete
  33. Hi Bob, This is really helpful post. Is similar kind of solution possible with custom Select Template button in VF Page? I have created custom button in in VF Page and when I click on that button it should open TemplateSelector page and after selecting template it should pass template to parent page (VF Page).

    Any workaround to it?

    ReplyDelete
  34. Hi Bob,

    I have used the code you have provided and used it. When I search the lookup field without giving any input (keep the lookup field blank and click on Go) It returns the error of runQuery().

    Please assist.

    ReplyDelete
  35. Hi Bob or anyone who can help.

    I cannot figure out why, on the Initial page to enter the account name, the inputfield is not enabled. I cannot type into it. Any help would be awesome...

    ReplyDelete
    Replies
    1. This is intentional. I didn't put in anything to allow you to type into the lookup and retrieve the associated id, so I added a handler to stop focus on the input field.

      Delete
  36. I resolved! Thank you

    ReplyDelete
  37. My lookup page just displays the headings? Any help would be great.








    {!account.Name}







    ReplyDelete
  38. I tried to post the page. Was not able.

    ReplyDelete
    Replies
    1. If you are having problems post to the Salesforce developer forums - I will likely see it, but so will loads of other developers who can help you.

      Delete
  39. That was a great example. Do you have an example of entering First and Last Name on the page. Pressing lookup passes both values to a popup window, which lists the passed values as headings and lists the Accounts in a table based on the 2 passed values? Window does not return anything.

    ReplyDelete
  40. it's not working for special character's like
    Contact requestors to correct mistakes on OR's

    ReplyDelete
  41. Hi Bob, Where are you passing idfield and namefield?

    ReplyDelete
  42. Hi Bob,
    Where are you instantiating namefield and idfield?

    ReplyDelete
  43. Popup button gets disappear in lightning.... it only works in classic. Can anybody tell the solution?

    ReplyDelete
  44. Greetings Bob Buzzard,

    I know that this post is "old" but GOLD! I am here to thank you for this tutorial! In fact, I am a trailblazer who started out fresh in doing Salesforce Programming.

    I was looking for this kind of solution because I would want to avoid the governor limits on SelectOption which is 1,000 records. But nevertheless, Thank you once again!

    By-the-way, I modified your code that renders the "Lookup" text into a "Lookup Icon;" so that, it would REALLY LOOK LIKE a "Lookup" field xD.

    ReplyDelete
    Replies
    1. Hi, pls how did you get the lookup icon?

      Delete