I was very excited when I found out that Salesforce was releasing Javascript remoting with Spring 11. There have been several cases where having access to Apex classes and logic in Javascript would make my development much easier. One area that I wanted to try out was in an autocomplete component that I built a while ago. I had hacked together a component that used a jQuery autocomplete plugin. While it worked, I had a couple of problems: it didn’t work in IE8 (everybody uses Chrome or Firefox, right?) and the autocomplete consumed a separate Visualforce page, so when I had developer mode turned on, it would choke on the wrapper that developer mode uses.
The Javascript Remoting feature is currently in Developer Preview, so my first step was to get it enabled for my org. A quick tweet to Josh Birk solved that. [Edit: As of Summer ’11, it is GA.] Using Josh’s blog post and the Salesforce documentation as a reference, I got to work.
First I created the controller. I made this controller as flexible as possible. You can pass it the API object name (Account, Contact, etc), the search string and any additional fields you want to include in the search. It will always search the Name field for the search string.
[Edit: I have created a new controller that uses apex-lang to build the Soql. You can find it in my blog post: https://verticalcode.wordpress.com/2011/02/21/using-apex-lang-to-build-soql-statements/.]
global class autoCompleteController { @RemoteAction global static SObject[] findSObjects(string obj, string qry, string addFields) { // more than one field can be passed in the addFields parameter // split it into an array for later use List<String> fieldList; if (addFields != null) fieldList = addFields.split(','); // check to see if the object passed is valid Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe(); Schema.SObjectType sot = gd.get(obj); if (sot == null) { // Object name not valid return null; } // create the filter text String filter = ' like \'%' + String.escapeSingleQuotes(qry) + '%\''; //begin building the dynamic soql query String soql = 'select id, Name'; // if an additional field was passed in add it to the soql if (fieldList != null) { for (String s : fieldList) { soql += ', ' + s; } } // add the object and filter by name to the soql soql += ' from ' + obj + ' where name' + filter; // add the filter by additional fields to the soql if (fieldList != null) { for (String s : fieldList) { soql += ' or ' + s + filter; } } soql += ' order by Name limit 20'; List<sObject> L = new List<sObject>(); try { L = Database.query(soql); } catch (QueryException e) { return null; } return L; } }
Next I created the component. Note that the component requires some jQuery resources which I pulled from the jQuery UI website and then uploaded the entire zip file into my resources in my org. [EDIT – I’ve found it is easier to just use Google’s CDN for the jquery files. You can replace the <apex:includeScript> section with this instead]:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"/> <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.12/jquery-ui.min.js"/> <apex:stylesheet value="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.12/themes/ui-smoothness/jquery-ui.css"/>
I also chose to use a spinner type indicator to queue the user that the autocomplete is working, which I uploaded as a resource.
<apex:component controller="autoCompleteController"> <!-- JQuery Files --> <apex:includeScript value="{!URLFOR($Resource.jqueryui189, 'js/jquery-1.4.4.min.js')}" /> <apex:includeScript value="{!URLFOR($Resource.jqueryui189, 'js/jquery-ui-1.8.9.custom.min.js')}" /> <apex:stylesheet value="{!URLFOR($Resource.jqueryui189,'css/smoothness/jquery-ui-1.8.9.custom.css')}"/> <!-- Attributes Required For Component --> <apex:attribute name="objectname" description="The object name you want to look for." type="String" required="true"/> <apex:attribute name="additionalfield" description="Any additional fields you'd like to search and include in the display." type="String" required="false"/> <apex:attribute name="autocomplete_textbox" description="The ID for the Autocomplete List Textbox." type="String" required="true"/> <style> .ui-autocomplete-loading { background: white url({!$Resource.circleIndicator}) right center no-repeat; } </style> <script type="text/javascript"> var j$ = jQuery.noConflict(); j$(document).ready(function() { var sObjects; var queryTerm; j$(esc('{!autocomplete_textbox}')).autocomplete({ minLength: 2, source: function(request, response) { queryTerm = request.term; autoCompleteController.findSObjects("{!objectname}", request.term, "{!additionalfield}", function(result, event){ if(event.type == 'exception') { alert(event.message); } else { sObjects = result; response(sObjects); } }); }, focus: function( event, ui ) { j$(esc('{!autocomplete_textbox}')).val( ui.item.Name ); return false; }, select: function( event, ui ) { j$(esc('{!autocomplete_textbox}')).val( ui.item.Name ); j$(esc('{!autocomplete_textbox}_lkid')).val( ui.item.Id ); j$(esc('{!autocomplete_textbox}_lkold')).val( ui.item.Name ); return false; }, }) .data( "autocomplete" )._renderItem = function( ul, item ) { var entry = "<a>" + item.Name; j$.each("{!additionalfield}".split(",") , function(key, value) { entry = entry + " " + item[value]; }); entry = entry + "</a>"; entry = entry.replace(queryTerm, "<b>" + queryTerm + "</b>"); return j$( "<li></li>" ) .data( "item.autocomplete", item ) .append( entry ) .appendTo( ul ); }; }); function esc(myid) { return '#' + myid.replace(/(:|\.)/g,'\\\\$1'); } </script> </apex:component>
Finally, I can use the component in any Visualforce page to autocomplete on any field. In the below example, I’m looking for account name or ticker symbol:
<apex:page> <apex:form><br /> <apex:inputText id="account"> <c:AutoComplete2 objectname="Account" additionalfield="TickerSymbol" autocomplete_textbox="{!$Component.account}" /> </apex:inputText> </apex:form> </apex:page>
Edit: Added link to code for a new controller using apex-lang and a little bit more error handling.
I spent all week writing something like this myself…yours is better so I’m using yours. Thanks.
Very cool!
Nice job!
Pingback: Using apex-lang to build Soql statements | Vertical Code
Daniel, Can you include the necessary items from your jQuery UI zip? That way, if I want to customize a download I don’t need to grab everything. TIA.
Hi Gene,
I’m not sure of all the dependencies, but I think you can grab just the UI Core and the autocomplete widget. I used the smoothness theme, but you could use any theme or build your own. I just pulled the quick download because I figured I’d want to play with everything eventually.
Daniel
After copying your code verbatim and modifying only the jQuery references, I get a brief spinning wheel when I enter text and then…nothing. No error, no results. Ideas? Anyone? Bueller? Bueller?
Hi Mauricio,
If there are no matches, then that’s what happens. It sounds like you’ve got jQuery working if you have the spinning wheel starting and stopping. Since it starts and stops, it makes me think the javascript call is complete or failed. Did you ask Salesforce to enable Javascript Remoting for your org? I’d try putting it in debug and stepping through the javascript to see if any errors appear.
Yes, JS remoting is enabled. The code did not compile until SFDC switched that on. And there ARE records that should be coming up. Thanks!
You can try looking at my other post with some updated code for this component using apex-lang. It has better error handling that you might be able to take out and use if you don’t want to use apex-lang to build the SOQL.
Weirdly enough – this code works if a debugger (Firebug) is attached but hangs if it is not.
So here’s the scoop: Works on IE8 and Chrome, hangs on Firefox 3.6.xx unless Firebug is running. Grr. NOT your bad!
Huh! That’s one of the reasons I used jQuery for this – they are supposed to have all those cross browser problems worked out. Good to know. I’ve got Firefox 4.0 and it works. Glad you got the code working. I hope it helps!
I presume that these lines are supposed to populate the named fields with the named values? If the fields exist on the page?
select: function( event, ui ) {
j$(esc(‘{!autocomplete_textbox}’)).val( ui.item.name );
j$(esc(‘{!autocomplete_textbox}_lkid’)).val( ui.item.Id );
j$(esc(‘{!autocomplete_textbox}_lkold’)).val( ui.item.name );
return false;
},
Oddly, the first line works but the latter two do not.
Yes, that’s correct. The second two lines are used if it is a lookup field. These are hidden fields that if aren’t filled in and there are multiple matches on the lookup field, it will redisplay the page asking you to select the correct one. For example, say the auto complete for a contact returns back John Smith and you have two John Smiths in the database, it would redisplay asking which one you want to use if the lkid and lkold are not populated.
I’m not sure why they don’t fill in for your case – I only know enough jQuery to get me in trouble!
Hi Daniel,
I used your code it is excellent. but in lookup if i enter name it was showing undefiend with name of account. How to remove that one already record exist with that name. And after selecting the value of searched dropdown list i want execute action funtion method, which event will fire after selecting the record value.I tried onselect,onchange not working.
I didn’t get jquery files which were you used, i am using jquery-1.5.1.min.js,jquery-ui-1.8.14.custom.min.js,css/south-street/jquery-ui-1.8.14.custom.css
Give me suggestion on this
thank you in advance
krish
Hi Krish,
I’m on vacation for the next week and don’t have my computer with me. I’ll take a look at this when I get back and let you know.
Hi Daniel,
I am unable to getting Product lookup related records in the searched dropdown list. see below my code :
It is very urgent to client. please suggest on this .
Hi Daniel,
Previous I posted apex code but it is not visible see my code
apex:inputField value=”{!a.Product__c}” onkeypress=”return noenter(event);” id=”prod”
c:autoCompleteController objectname=”Product2″ additionalfield=”TickerSymbol” autocomplete_textbox=”{!$Component.prod}”
/apex:inputField
thanks for reply
krish
The additionalfield parameter doesn’t look right. If you need to query a second field you use this, otherwise, leave it out. There is no field in Product2 called TickerSymbol.
In regards to your question on jquery files, I’ve since found that it is easier to use Google’s hosted jquery code rather than use static resources. Replace it with this:
Ah, WordPress ate my code too! I’ve edited the post with the additional info.
Hi
I want to display Additional field in textbox which is coming from jquery.
I have used the same code.
But I get only the Name value of object.
Please guide me.
The example code will the add additional fields to the end for the autocomplete, but when you select the item, it only puts the name of the object into the textbox. I’m doing this because in my case, I was using it on a lookup field and didn’t want it to cause problems on the save. If you don’t have a lookup and just a textbox, then you could add the additional fields You’d need to modify this part of the code. This is where I am putting the values of the selected item into fields on the page.
select: function( event, ui ) {
j$(esc('{!autocomplete_textbox}')).val( ui.item.Name );
j$(esc('{!autocomplete_textbox}_lkid')).val( ui.item.Id );
j$(esc('{!autocomplete_textbox}_lkold')).val( ui.item.Name );
return false;
},
The variable “cl”, referenced as below, seems to be undefined.
var ele=document.getElementById(cl);
ele.click();
You’re right! That snuck in on my copy and paste. Obviously, the example page is incomplete. It would have a submit button on it in real life. I use the noenter function to choose which button to click on the page when Enter is pressed. Rather than try to explain it, here’s a discussion board post on the topic: http://boards.developerforce.com/t5/Visualforce-Development/Bug-CommandButton-not-submitting-a-form-when-Enter-Key-pressed/td-p/153492
Pingback: Email AutoComplete (using jQuery) | Perspectives on Salesforce.com
This is not working in IE8 . Any solution for this.
Most of the users are using IE8. if any solution we can use this.
thanks,
RR.
Hi Ravi,
I don’t have access to IE8 anymore, but it did work when I tested it a while ago. Have you tested in other browsers? Have you confirmed that JavaScript is enabled in your browser? Since it uses jQuery, I don’t have any alternatives. They usually do a good job of supporting all the browsers.
I was just able to test it on IE8 and the autocomplete worked for me. I’m not sure why it isn’t working for you. Sorry I can’t be more help.
Daniel, I had everything set correctly. What it ended up being is that I had to prefix the Apex class with my namespace. Working now!! Thanks for your help.
Hello,
Everything builds, but when I am executing the script on my webpage, I am receiving the JavaScript error “‘autoCompleteController’ is undefined” on the autoCompleteController.findSObjects(, I am running API Version 23.0. Any ideas on what this could be?
Any help would be appreciate.
G. Frank
Have you created your Apex class and made sure to call it autoCompleteController? JavaScript is case sensitive, so check your upper and lower case. If that isn’t it, then I’m stumped.
This is a SalesForce known issue that was introduced with W’12 that they have apparently yet to issue a patch for. They gave us two workarounds:
1) “A workaround is for the customer to manually create the Javascript proxy w/ the known namespace.
if (MyNs) {
window.MyNs = {};
window.MyNs.MyController = MyController;
}”
We did not use this one.
2) Add empty constructors to the top of the class you are using to control the autocomplete (ie the one with the @RemoteAction notation). For example:
// SFDC TEST – Added empty constructor to allow class to be used as a VF controller extension
global AutoCompleteController() {}
global AutoCompleteController(ApexPages.StandardController controller) {}
where “AutoCompleteController” is YOUR class name, then add that class name to the extensions list on the VF page(s) you have the autocomplete “widget” on.
We used this one and it works.
Thanks! Good to know.
Hello,
Where can on get he circleIndicator resource from?
You can generate one at this great site: http://www.ajaxload.info/
Hello,
It seems that the variable ‘cl’ in ‘var ele=document.getElementById(cl);’ is undefined..
Am I missing something?
No, that was an artifact from copy and paste. I’ll modify the example code to get rid of it.
Daniel,
The list of items for the user to select from is generated as links below the input box.
What is the reason that you didn’t choose a more “traditional” approach, such as the one demoed on http://docs.jquery.com/UI/API/1.8/Autocomplete#overview (or Google search for that matter)?
Thanks,
Rafi,
If you are seeing the list of items, then it sounds like the correct CSS isn’t being used. Make sure you’re either referencing the hosted style sheet by jQuery or uploading one to your your resources in Salesforce.
Thanks, Daniel. Indeed i used the wrong CSS. Works as expected! Well done!
Daniel,
I found another interesting issue in the code below:
entry = entry.replace(queryTerm, “” + queryTerm + ““);
It is basically case sensitive, so the BOLD works only on exact match,where as the look-up is not case sensitive.
so for example, if you type “un” the item “United Oil” will not get any bold, wheres “Get united” will.
I believe this fix may work for most cases:
entry = entry.replace( new RegExp( “(” + queryTerm + “)” , ‘gi’ ), “$1” );
Will not work well if special characters are used in the search string (such as &)
Thanks Rafi! I meant to put a disclaimer in that the replace wasn’t very robust. Great solution!
I had to make it:
entry = entry.replace( new RegExp( “(” + queryTerm + “)” , “gi” ), “$1” );
to get it to work properly..
i am not able to get circle-Indicator. Can anyone help me out?
I have downloaded jquery zip file and set as static resource. But no luck.
Error: Static Resource named circleIndicator does not exist. Check spelling
I didn’t provide that as a resource and it doesn’t come with jQuery. If you go to http://www.ajaxload.info/ and click generate, it should create one suitable for this. Then upload it as a static resource name it circleIndicator.
Hi Daniel,
I’m receiving this error:
Uncaught TypeError: Cannot set property ‘_renderItem’ of undefined
Something wrong?
Just switched to Chrome 4.0 and works fine
Hi Daniel,
When i omitted the additionalfield paramter, the query builder did not handle the space. It was forming a query with “,” immediately followed by “from”. So I added the condition in autoCompleteController for handling spaces and it worked.
if (addFields != null && addFields != ”)
Aravinda
Hi Daniel,
i have a custom object named Product_Master. when i use this to your
it returns null, for this
Map gd = Schema.getGlobalDescribe();
Schema.SObjectType sot = gd.get(obj);
if (sot == null) {
System.debug(‘object null’);
// Object name not valid
return null;
}
i have no idea why this code returns null; for your note, when i change the objectname to Account
it works.
please help..
thanx for your attention
sorry the apex code wasnt written in my recent comment
“i have a custom object named Product_Master. when i use this to your”
||
||
i have no idea why this code returns null; for your note, when i change the objectname to Account
||
||
Hi Herianto,
Are you using the API name for the object? It should have __c at the end. Try using Product_Master__c.
Hi,
I wanted to know which Piece to change If I have to make it work for the first character as well, currently I have to enter 2 characters to make it work.
You’d change that in the JavaScript. Look for the section that says “minLength: 2,”.
Hi
Nice work on this. I had one question.
On IE, FF, Safari the type ahead suggestion list works as expected (uses the Jquery theme to display in a list with colored background). On Chrome, it appears as a bulleted list with transparent background. Any ideas why the CSS does not pick up on Chrome (22.0.1229.94) on Win 7. I’ve also tried on a Mac with the same issues.
I am using the following as includes:
thanks
Craig
I just tried it on Chrome on my Mac and it works. The comment cut out your includes, but try updating your includes.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"/>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"/>
<apex:stylesheet value="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/ui-lightness/jquery-ui.css"/>
Hi
Thanks for your quick reply. Its working now, I figured out the issue. In the include code you had in your original post, the Google CDN stylesheet include was http. Changing to https fixed the issue.
Craig
Pingback: Email AutoComplete (using jQuery) | WikiCloud
Hi Daniel: Many thanks for sharing your solution. I was able to follow your instruction to implement the solution in our org although I am a 1000% dummy with both Salesforce and programming.
Your solution works like a charm with Account auto-complete. However I was not able to make it working with other lookup fields.
For example:
This is the code that I use for Account auto-complete, which works fine
This is the code that I use for Contact auto-complete, which doesn’t propose any auto-complete list
Please kindly help!
Thanks and wish you a very Happy 2013!
Bing Maletz
Here again the code ( I removed all starting/ending brackets)
This is the code that I use for Account auto-complete, which works fine
apex:inputfield value=”{!eQuote__c.Soldto_Account__c}” id=”Account”
c:AutoComplete objectname=”Account” additionalfield=”SAP_Customer_ID__c” autocomplete_textbox=”{!$Component.account}”
apex:inputfield
This is the code that I use for Contact auto-complete, which doesn’t propose any auto-complete list
apex:inputfield value=”{!eQuote__c.Contact__c}” id=”Contact”
c:AutoComplete objectname=”Contact” autocomplete_textbox=”{!$Component.Contact}”
apex:inputfield
I found the solution.
1) For the Contact lookup, I initially set “Account” as “additionalfield”, which doesn’t work. I changed it to “Email”, then it works. I assume that the “additionalfield” can not be a lookup field
2) For the other lookup fields, I initially omitted “additionalfield”, then it doesn’t work. After I added an “additionalfield”, then it works.
Now everything works and it looks really gorgeous..
Thanks again!
Your code works so perfectly !
I am getting & for & and " for double quotes in account fields when i see them in autocomplete drop down.
I dont know why these characters are getting converted automatically to the special character form and not getting converted back to their original form. Any clue?