function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Jon KeenerJon Keener 

Creating a Visualforce Popup window that operates similar to the Lookup popup windows

I'm starting to do some research into what it would take to create a Visualforce popup window similar to the standard salesforce lookup windows.  For now, my intention is to try to come up with a stronger lookup window for one of our custom objects, and at the moment I plant to create a link on a visualforce page to bring the popup up.  (This link will be right next to the existing standard salesforce lookup icon.)
 
I figured I'd start by seeing if anyone else has had any success doing something like this.  The items I am going to tackle first are:
 
1.  Create the popup window.  -  Don't expect this to be a problem.
2.  Pass parameters to the popup through the URL - Don't expect this to be a problem.
3.  Emulate the existing functionality of an existing Salesforce lookup, so that the lookup window will disappear automatically if the parent window is reselected - Not sure how to implement this one yet
4.  Pass a value, in this case, an Id back to the parent window - Not sure how I'm going to do this, but I'm going to attempt to pass some sort of callback function into the popup so it can return a value
 
Being able to do something like this leads to the need to be able to override standard Salesforce lookup functionality.  That way this could be easily implemented on standard pages, in addition to VisualForce pages.
 
If anyone has attempted something like this with Visualforce yet, any learnings would be great!
 
Thanks!
 
Jon Keener
mtbclimbermtbclimber
Jon,

Can you describe what it is about your lookup interface that is different?

We are considering designs for type-specific inputField elements and your input would be tremendously helpful.

Thanks,
Jon KeenerJon Keener
Andrew,
At the moment, I've been focused on the items in the previous message, mainly trying to prove that the basic framework is doable via Visualforce, prior to actually designing the actual lookup interface. 
 
In our case, what I'm looking at this for is a lookup against a specific custom object for product materials.  In our case, we have over 500,000 product materials that are being searched.  The current lookup functionality is just not effective for a user to search, unless they are pretty much exactly sure of the material name.  In our case, these materials are arriving from an SAP system so our users typically have to toggle between SAP and Salesforce and copy/paste the material name to get the lookup to find what they need. 
 
That being said, here are some of the reasons/functionality enhancements I'd like to try to add:
 
1.  The ability to pass in "Initial Filter Criteria". 
  • The key is that this criteria would be automatically applied behind the scenes and the end user would not know it's really being applied. 
  • For our material item above, based on information on the Customer account, I could greatly limit the number of potentially valid materials to only ones that might be valid for the specific customer they are on.  There would still be a lot, but a lot less then 500,000.
  • This would be useful for a lot of other scenarios too.  One Data Cleansing issue we run into a lot is with account/contact/opportunity lookup fields.  One rule that would be nice to be able to implement for these would be to only show the contacts related to a specific account when creating a task related to that specific account.  There are probably some interesting user lookup filters that would be useful too.

2.  Default the search to a "LIKE" vs. "starts with"

  • Users, being more used to Google style searching are more used to searching anywhere within the name, vs the "starts with" behavior.
  • I would probably set it up to do the like automatically unless they specifically included a wildcard.

3.  Search across multiple fields

  • It's extremely limiting on Custom Objects to only search the name field, especially when in the results they see a lot more fields that they would like to search on
  • In our specific case with Materials - we have a separate material number field, and a number of other categorization fields that would be useful to search on

4.  Add some basic filters

  • The categorization fields in the last one might be better set up as filters, sort of like an excel filter

5.  Include a drop down (similar to whats in print preview right now) to allow the user some flexibility on the number of results returned

6.  It might be interesting to have a "recent items" list on the lookup pages for each specific object  (This one is probably better for ideas, because I don't think I'd try to implement this one myself...)

 

Overall, that's a quick synopsis of the kinds of things I would be trying to tackle on a custom lookup.  I think functionality-wise, the items above should be pretty doable (some generically/re-usable, some not) in Visualforce/APEX.  The tougher part to me is the whole pop-up functionality, and whether that part is doable, hence my previous post. 

Of course, I've love to see all of this put into standard Salesforce.  I'm sure there's ideas in ideas.salesforce.com for most of these.  I'm just curious of what might or might not be possible with popups in Visualforce, because it could lead to some interesting things beyond lookups.

Jon Keener

Shwetal DesaiShwetal Desai
Hi Jon,
 
m also trying to create custom lookup page which work same as standard lookup page provided by salesforce, My application requirements are mostly same as urs...
 
But here is the points which m not able to accomplished yet,
 
1.  Pass parameters to the popup Page - Don't expect this to be a problem. (Through URL m able to do this)
2.  Emulate the existing functionality of an existing Salesforce lookup, so that the lookup window will disappear automatically if
     the parent window is reselected.
3.  Return a selected Id and Name value back to Parent window on the specific Text control - (m Not able to do this)
 
Please guide me if u get success on these.
 
--
Shwetal
 
EvanCEvanC
Did anyone have luck with this?  I am trying to do the same.
LegetzLegetz
Jon, it was nice to read your post as I have been struggling with the same sub-problem:
"It's extremely limiting on Custom Objects to only search the name field, especially when in the results they see a lot more fields that they would like to search on"

At first, it was hard to realise that the lookup window is badly limited what comes for searching.

One example from my organisation:
- We have custom object "Resource" with following fields:
Resource ID: autonumber RES-{0}
Resource firstname: Text
Resource lastname: Text
External resource: Checkbox 
- If i want to find resource "John Doe", I need to know John's autonumerated value RES-123
- I cannot search using lastname field although that would be the best choice for limiting search results.


Message Edited by Legetz on 01-19-2009 01:18 AM
grigri9grigri9
Has anyone been able to get somewhere with recreating the lookup window functionality? We're trying to pre-filter the results in a lookup window.
phanthongphanthong

The harderst part is to pass the value/id back to the parent window.

It can be done using javascript. And you can reuse the function that SFDC is using for there lookup page

It's  lookupPick() located in functions.js

View the page source of saleforce lookup page will give you some ideas of how to use this function

 

MukulMukul

Hey there,

 

I am trying to achieve something similar. Can you please give me more pointers on it?

 

Thanks much!

 

Regards

Mukul

sfdcfoxsfdcfox

I think I would implement this as two visualforce pages (or, you could be sneaky and make it just one, but we'll keep it simple).

 

To implement a custom lookup window, you'd have to do the following:

 

1) Use window.open to open a new window.

 

2) You can pass paramters via tha url and grab them in the recipient frame using ApexPages.currentPage().getParameters().get('param') syntax.

 

3) Set a "window.onfocus" handler in the parent window. Have the callback for this function close the frame window if it's open.

 

4) A function in the parent window can be created with one parameter, a string value. The popup window can call window.opener.someFunction(value) to pass the value back to the parent frame. someFunction can also close the popup window (much like in #3). An alternative method would be to set a window.onblur method in the popup, and that could cause it to close if it loses focus for any reason at all.

 

While I'm entirely too non-energetic to actually write a working example tonight, I know it can be done. I wrote a custom list button once that allowed you to reassign opportunity owners using the standard lookup functionality. Actually, I still have that code... I'll post it here. I apologize for its programming style, it's not exactly what I described, but that's only because it uses the standard Salesforce lookup window, so I had to be accomodating (amazingly, I wrote this in Winter '08 and it still works).

 

 

{!RequireScript("/js/functions.js")} var recordsToTransfer = {!GetRecordIds($ObjectType.Opportunity)} function createForm() { form = document.createElement('form') form.name = 'lookupform' form.id = 'lookupform' document.body.appendChild(form) } function addElement(name,value) { form = document.getElementById('lookupform') element = document.createElement('input') element.name = name element.id = name element.type = 'hidden' element.value = value form.appendChild(element) } function doAction() { window.removeEvent(window,'focus',doAction,true) userToTransferTo = document.getElementById('lookup_lkid').value if(userToTransferTo == '') return recordArray = new Array() for(recordToTransfer in recordsToTransfer) { var Opportunity = new sforce.SObject("Opportunity") Opportunity["Id"] = recordsToTransfer[recordToTransfer] Opportunity["OwnerId"] = userToTransferTo recordArray.push(Opportunity) } try { results = sforce.connection.update(recordArray) } catch(e) { alert(e) } window.top.location.href = window.top.location.href } function setupTransfer() { if(recordsToTransfer.length < 1) { alert('Please select at least one row!') return } if(!document.elementsAddedToPage) { createForm() addElement('lookup_lkid','') addElement('lookup_lkold','') addElement('lookup_lktp','005') addElement('lookup_lspf','0') addElement('lookup_mod','0') addElement('lookup','') document.elementsAddedToPage = true } openLookup( '/_ui/common/data/LookupPage?lknm=lookup'+ '&lkfm=lookupform&lkrf=&sn=1&lktp='+ document.getElementById('lookup_lktp').value, 670, document.getElementById('lookup_mod').value, '&lksrch='+ escapeUTF(document.getElementById('lookup').value), 'maxw') window.addEvent(window,'focus',doAction,true) } setupTransfer()

 As always, never know wheninteral APIs will change, so don't use thiscode verbatim in a system you care about (i.e. production). However, it should serve as a nice example for the basis of a lookup window. If you have any questions about it, feel free to send me a private message.

 

MukulMukul

Hi sfdcfox,

 

Thanks for the reply.. I tried doing that but i am stuck at one point. I dont know how to get the checkbox values and pass it back to the parent form:

 

Here is the snippet that calls the child window:

 

<apex:column > <span id="filter_lookup0" style="{!showFilter}"><img src="/s.gif" alt="Lookup (New Window)" onClick="javascript:window.open('/apex/lookUpWindow?lookup={!fieldName}', 'lookupwindow', 'height=500, width=700,resize=0')" title="Lookup (New Window)"/></span> </apex:column>

 

 

 

 

Here is my Visual force page code for the lookup window:

 

<apex:page controller="newScoreRuleController"><apex:form id="lookup"><apex:pageBlock ><apex:dataTable value="{!lookupList}" var="leadOutput" styleClass="tableClass list" rowClasses="odd,even"> <apex:column ><apex:facet name="header">Value</apex:facet><apex:outputText value="{!leadOutput.Industry}" id="valLookup"/></apex:column><apex:column ><apex:facet name="header">Select</apex:facet><apex:inputCheckBox value="{!leadOutput.Industry}" id="selectedLookUpVal"/></apex:column></apex:dataTable><apex:pageBlockButtons ><script type="text/javascript">function insertSelectedRecords() { var e= document.getElementById('{!$Component.lookup}').elements.length; var cnt=0; var reg = new RegExp('selectedLookUpVal'); for(cnt=0;cnt<e;cnt++) { if(reg.test(document.getElementById('{!$Component.lookup}').elements[cnt].name)){ //alert('here'); if (document.getElementById('{!$Component.lookup}').elements[cnt].checked) { // Get the value in a string and return it to the parent window....(??) alert(document.getElementById('{!$Component.lookup}').elements[cnt].value); //alert(document.getElementById('{!$Component.valLookup}').value); } } }// var inserteddata = document.getElementById('{!$Component.selectedLookUpVal}').checked;}</script><apex:commandButton value="Insert Selected" onClick="javascript:insertSelectedRecords();"/></apex:pageBlockButtons></apex:pageBlock></apex:form></apex:page>

 here is my controller code:

 

public List<Lead> getLookUpList() {String lookupParam = ApexPages.currentPage().getParameters().get('lookup'));String Qry = 'select ' + lookupParam + ' from lead where ' + lookupParam + ' != null';List<Lead> P = Database.Query(Qry); return P; }

 

 Please help!!!

 

Regards

Mukul 

 

 

 

DevNVDevNV

To include custom fields into a search you can make them External Ids.  This setting on the field definition will add the field into the sidebar search (and presumably the lookup searches as well as Advanced Search).  You can only have 3 custom fields in an object with this setting as it indexes the table but it's helpful when you are using the autonumber type of object name and want to look up items in your case by resource firstname or resource lastname

 

Niki

MohandaasMohandaas

Has anyone been able to accomplish this task?

 

How do we pass the selected value back to the parent form.

notsosmartnotsosmart

I used the link below to learn how to build this.  The build was easy and the code is easily adaptable for more sophistication. For 1 I Initialized the search string as it's stupid for it to default that of the current record requiring the user to clear it and press go.  Likewise, I intend the expand the search logic as well.

 

I still have 2 issues:

1.  I used field sets as suggested but the page for NEW is empty except for Tilte & Save button.

    I there another set to acess field sets after defining them?

2.  I don't see how to associate it and override the standard lookup inherited with the Object of my page.

 

 

Any ideas.  I hope this works for some of you.

 

http://blog.jeffdouglas.com/2011/08/12/roll-your-own-salesforce-lookup-popup-window/

fourfourfunfourfourfun

Hi notsosmart, I'm actually trying to get that code from Jeff's blog to work. I am struggling due to limited ability to even get it to run mimicking the example. No chance you could help out is there?

 

I can see what the code set is doing BUT the thing I cannot see is how on earth that jQuery knows how to intercept the click. I can't see what I have to edit (considering I am trying to do the same code as Jeff) to make it work. At the moment, nothing different happens on click.

Sagar Nagvekar 14Sagar Nagvekar 14
// The controller for main VF page vfAssignNewAccountToContact
public with sharing class AssignNewAccountToContact
{
    public Contact c {get;set;}
    public String selectedAccountName   {get;set;}
    public String selectedAccountId {get;set;}
 
    public AssignNewAccountToContact()
    {
        selectedAccountName = '';
        selectedAccountId = '';
        c = [select id, name from Contact where id = :ApexPages.currentPage().getParameters().get('id')];
        System.debug('////////////c is ' + c);

    }
    
    public pageReference changeTheAccount()
    {
        System.debug('//////////selectedAccountId is ' + selectedAccountId);
        System.debug('//////////selectedAccountName is ' + selectedAccountName);
       
        if(selectedAccountId != '')
        {
            System.debug('//////////selectedAccountId is not null, so proceeding ');
            c.accountId = selectedAccountId;
            update c;
            ApexPages.addMessage(new ApexPages.message(ApexPages.severity.info, 'New account assigned successfully'));
            
            /*
              Make variables null, so that if something is typed now in the textbox,
              it gives error message if "Assign new account" is clicked again.
            */
            selectedAccountId = '';
            selectedAccountName = '';
        }
        else
        {
            System.debug('//////////selectedAccountId is null');
            ApexPages.addMessage(new ApexPages.message(ApexPages.severity.error, 'Kindly select an account by clicking on Lookup'));
        }
        
        return null;
    }
}

<!--
  VF page name :- vfAssignNewAccountToContact
  Main VF page for assigning new account to a contact. Go to an existing contact record and add
  /apex/vfAssignNewAccountToContact?id=<ContactRecordID> to the URL to view this VF page.
-->
<apex:page controller="AssignNewAccountToContact">
  <apex:form >
    <apex:PageMessages id="messages"/>
    Click on Lookup to select a new account for this contact and then click on "Assign new account" 
   <apex:pageBlock title="Contact Details">
      Contact name: {!c.name}
      <br/>
      <apex:pageBlockSection>
        <apex:inputText value="{!selectedAccountName}" id="accountName" label="Account"/>
        <a onclick="popUp();" style="cursor: pointer;">Lookup</a>
        <apex:inputHidden value="{!selectedAccountId}" id="accountId"/>
          
        <script>
            var objInputName = '{!$Component.accountName}';
            var objInputId = '{!$Component.accountId}';
                    
            function popUp() 
            {
              var objAccountInput = document.getElementById(objInputName);
              //alert('parameter is ' + document.getElementById(objInputName).value);
              
              var urlAdd = 'vf_AccountLookup_Controller';
              if(objAccountInput.value.length > 0)
              {
                urlAdd += '?srch=' + objAccountInput.value;
                //alert('urlAdd is ' + urlAdd);
              }
              var newWindow = window.open(urlAdd);
            }
        
            function setAccount(recordId,recordName)
            {
              //alert('recordId is ' + recordId);
              //alert('recordName is ' + recordName);
              document.getElementById(objInputName).value = recordName;
              document.getElementById(objInputId).value = recordId;
            }
        </script>
        <br/>
        <apex:commandButton value="Assign new account" action="{!changeTheAccount}"/>   
      </apex:pageBlockSection>
    </apex:pageBlock>
  </apex:form>
</apex:page>

// The controller for the lookup popup box for selecting new account for a contact.
public class AccountLookup_Controller
{
    public String searchText {get;set;}
    public List<Account> searchResults {get;set;}
    
    public AccountLookup_Controller()
    {
        // get search parameter from url if present
        if(ApexPages.currentPage().getParameters().containsKey('srch'))
        {
            searchText = ApexPages.currentPage().getParameters().get('srch');
        }
        else
        {
            searchText = '';      
        }
        searchResults = [Select Id, Name, AccountNumber, Site From Account Where Name Like : searchText + '%'];
    }
    
    public void searchRecords()
    {
        searchResults = [Select Id, Name, AccountNumber, Site From Account Where Name Like : searchText + '%'];
    }  
}

<!--
  VF page name :- vf_AccountLookup_Controller
  The VF page for the lookup popup box which appears for selecting new account for a contact.
-->
<apex:page controller="AccountLookup_Controller" showHeader="false" sidebar="false">
  <apex:form >
    <apex:pageBlock >
      <apex:sectionHeader title="Select the account to be associated with the contact"/>
      <apex:inputText value="{!searchText}" onkeyup="searchImmediately()"/> &nbsp;
      <apex:commandButton value="Go" action="{!searchRecords}"  
          rerender="results"/>
      
      <apex:actionFunction name="searchImmediately" action="{!searchRecords}" reRender="results"/>
      
      <apex:pageBlocksection title="Account Search Results" id="results">
        <apex:pageBlockTable value="{!searchResults}" var="a"
            rendered="{!searchResults.size>0}">
          
          <apex:column headerValue="Name">
            <a style="cursor:pointer;"  
                    onclick="window.opener.setAccount('{!a.Id}', '{!a.Name}');
                self.close();">
                {!a.name}
            </a>
          </apex:column>
          
          <apex:column headerValue="Account Number" value="{!a.AccountNumber}"/>
          <apex:column headerValue="Account Site" value="{!a.Site}"/>      
        </apex:pageBlockTable> 
            
        <apex:outputText value="No results found"   
            rendered="{!searchResults.size=0}"/>
      
      </apex:pageBlocksection>
    </apex:pageBlock>
  </apex:form> 
</apex:page>

 
Sagar Nagvekar 14Sagar Nagvekar 14
Added functionality for showing new popup window as a popup box, and also for closing this popup box automatically whenever it loses focus (goes into background). Thereby making it more similar to standard Salesforce popup box which appears while assigning a new account to a contact.
// The controller for main VF page vfAssignNewAccountToContact
public with sharing class AssignNewAccountToContact
{
    public Contact c {get;set;}
    public String selectedAccountName   {get;set;}
    public String selectedAccountId {get;set;}
 
    public AssignNewAccountToContact()
    {
        selectedAccountName = '';
        selectedAccountId = '';
        c = [select id, name from Contact where id = :ApexPages.currentPage().getParameters().get('id')];
        System.debug('////////////c is ' + c);

    }
    
    public pageReference changeTheAccount()
    {
        System.debug('//////////selectedAccountId is ' + selectedAccountId);
        System.debug('//////////selectedAccountName is ' + selectedAccountName);
       
        if(selectedAccountId != '')
        {
            System.debug('//////////selectedAccountId is not null, so proceeding ');
            c.accountId = selectedAccountId;
            update c;
            ApexPages.addMessage(new ApexPages.message(ApexPages.severity.info, 'New account assigned successfully'));
            
            /*
              Make variables null, so that if something is typed now in the textbox,
              it gives error message if "Assign new account" is clicked again.
            */
            selectedAccountId = '';
            selectedAccountName = '';
        }
        else
        {
            System.debug('//////////selectedAccountId is null');
            ApexPages.addMessage(new ApexPages.message(ApexPages.severity.error, 'Kindly select an account by clicking on Lookup'));
        }
        
        return null;
    }
}

<!--
  VF page name :- vfAssignNewAccountToContact
  Main VF page for assigning new account to a contact. Go to an existing contact record and add
  /apex/vfAssignNewAccountToContact?id=<ContactRecordID> to the URL to view this VF page.
-->
<apex:page controller="AssignNewAccountToContact">
  <apex:form >
    <apex:PageMessages id="messages"/>
    Click on Lookup to select a new account for this contact and then click on "Assign new account"
    <apex:pageBlock title="Contact Details">
      Contact name: {!c.name}
      <br/>
      <apex:pageBlockSection >
        <apex:inputText value="{!selectedAccountName}" id="accountName" label="Account"/>
        <a onclick="popUp();" style="cursor: pointer;">Lookup</a>
        <apex:inputHidden value="{!selectedAccountId}" id="accountId"/>
          
        <script>
            var objInputName = '{!$Component.accountName}';
            var objInputId = '{!$Component.accountId}';
                    
            function popUp() 
            {
              var objAccountInput = document.getElementById(objInputName);
              //alert('parameter is ' + document.getElementById(objInputName).value);
              
              var urlAdd = 'vf_AccountLookup_Controller';
              if(objAccountInput.value.length > 0)
              {
                urlAdd += '?srch=' + objAccountInput.value;
                //alert('urlAdd is ' + urlAdd);
              }
              var newWindow = window.open(urlAdd,'Popup','height=500,width=700,left=150,top=50,resizable=no,scrollbars=yes,toolbar=no,status=no');
            }
        
            function setAccount(recordId,recordName)
            {
              //alert('recordId is ' + recordId);
              //alert('recordName is ' + recordName);
              document.getElementById(objInputName).value = recordName;
              document.getElementById(objInputId).value = recordId;
            }
        </script>
        <br/>
        <apex:commandButton value="Assign new account" action="{!changeTheAccount}"/>   
      </apex:pageBlockSection>
    </apex:pageBlock>
  </apex:form>
</apex:page>

// The controller for the lookup popup box for selecting new account for a contact.
public class AccountLookup_Controller
{
    public String searchText {get;set;}
    public List<Account> searchResults {get;set;}
    
    public AccountLookup_Controller()
    {
        // get search parameter from url if present
        if(ApexPages.currentPage().getParameters().containsKey('srch'))
        {
            searchText = ApexPages.currentPage().getParameters().get('srch');
        }
        else
        {
            searchText = '';      
        }
        searchResults = [Select Id, Name, AccountNumber, Site From Account Where Name Like : searchText + '%'];
    }
    
    public void searchRecords()
    {
        searchResults = [Select Id, Name, AccountNumber, Site From Account Where Name Like : searchText + '%'];
    }  
}

<!--
  VF page name :- vf_AccountLookup_Controller
  The VF page for the lookup popup box which appears for selecting new account for a contact.
-->
<apex:page controller="AccountLookup_Controller" showHeader="false" sidebar="false">
  <apex:form>
    <script>
      window.onblur = function() {
          // close this popup window if it loses focus, that is, goes into the background
          self.close();
      }
      document.onblur = window.onblur;
      document.focus = window.focus;
    </script>
    <apex:pageBlock>
      <apex:sectionHeader title="Select the account to be associated with the contact"/>
      <apex:inputText value="{!searchText}" onkeyup="searchImmediately()"/> &nbsp;
      <apex:commandButton value="Go" action="{!searchRecords}"  
          rerender="results"/>
      
      <apex:actionFunction name="searchImmediately" action="{!searchRecords}" reRender="results"/>
      
      <apex:pageBlocksection title="Account Search Results" id="results">
        <apex:pageBlockTable value="{!searchResults}" var="a"
            rendered="{!searchResults.size>0}">
          
          <apex:column headerValue="Name">
            <a style="cursor:pointer;"  
                    onclick="window.opener.setAccount('{!a.Id}', '{!a.Name}');
                self.close();">
                {!a.name}
            </a>
          </apex:column>
          
          <apex:column headerValue="Account Number" value="{!a.AccountNumber}"/>
          <apex:column headerValue="Account Site" value="{!a.Site}"/>      
        </apex:pageBlockTable> 
            
        <apex:outputText value="No results found"   
            rendered="{!searchResults.size=0}"/>
      
      </apex:pageBlocksection>
    </apex:pageBlock>
  </apex:form> 
</apex:page>