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.

34 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. Erin Shannon1 June 2013 00:33

      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. Erin Shannon31 May 2013 01:05

    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