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
here.
Simply unzip into the src directory of your Force.com project in the
IDE.