The following is not officially support, so be careful, and is applied to all requests. Also, be aware that Salesforce.com's general HTTP request timeout may come into play.
<script>
_Visualforce.Ajax.timeout=120000;
...
</script>
And, BTW, Visualforce Remoting is a popular feature of Visualforce.
The reason it functions properly when you remove the rerender
is because of the way the javascript events are bound in the browser to the HTML elements in the page. By removing the rerender attribute and causing a full page post-back, the whole page is loaded from scratch and all of the script is evaluated and bound to the targeted elements in the DOM.
A rerender attribute causes parts of the page structure to be replaced when the response comes back. Because of this, when you bind a javascript function to an HTML element and then later you replace that element, any function that was previously bound isn't ever going to be called again. The element in the DOM that the function was originally bound to doesn't exist. It has been replaced with a new element which may have identical markup, but the new element does not have any of the old element's events bound to it.
In order to make this operate the way you want it to you will want to move all of the autocomplete bindings out of the ready
function and into a function defined separately which you can call first within the ready
function and then again when the rerender
is complete. This will give you control of the bindings on both the 'on initial display' and 'after replacing part of the page' parts of this lifecycle.
One other thing to note, you don't need to use the function esc(SFDC_ID)
in your markup in order to escape the colons in the salesforce ID values that are being passed into the component. You can target the ID attribute specifically and put the string containing the ID with colons in quotes.
The selector will look like this: q$('[id="{!autocomplete_textbox}"]').autocomplete({
Example of the way your component markup could be structured:
<script type="text/javascript">
var bindAutoComplete = function () {
// beware that this AutoCompleteInputElement on the next line without a var in front of it
// is going to create a global variable on the window object, which is generally unadvisable
AutoCompleteInputElement = function () {};
q$('[id="{!autocomplete_textbox}"]').attr('onchange', '');
var sObjects;
var queryTerm;
q$('[id="{!autocomplete_textbox}"]').autocomplete({
minLength: 1,
source: function (request, response) {
queryTerm = request.term;
AutocompleteController.findSObjectsSingle("{!objectname}", "{!additional_filter}", "{!FieldsToDisplay}", "{!FieldsToQuery}", "{!limit}", request.term, function (result, event) {
if (event.type == 'exception') {
alert(event.message);
} else {
sObjects = result;
response(sObjects);
}
});
},
focus: function (event, ui) {
//q$('[id="{!autocomplete_textbox}"]').val( ui.item.Name );
return false;
},
select: function (event, ui) {
q$('[id="{!autocomplete_textbox}"]').val(HtmlDecode(ui.item.Name));
q$('[id="{!autocomplete_textbox}_lkid"]').val(ui.item.Id);
q$('[id="{!autocomplete_textbox}_lkold"]').val(ui.item.Name);
q$('[id="{!autocomplete_textbox}_mod"]').val('1');
return false;
}
})
.data("uiAutocomplete")._renderItem = function (ul, item) {
var fields_to_display = '{!FieldsToDisplay}'.split(',');
var display_items = [];
for (i in fields_to_display) {
if ((i / 2) * 2 == i) {
fieldBreakDown = fields_to_display[i].split('.');
tempItem = item;
for (j in fieldBreakDown) {
if ((j / 2) * 2 == j) {
tempItem = tempItem[fieldBreakDown[j]];
}
}
display_items.push(tempItem);
}
}
var display_item = display_items.join(' - ');
var temp = queryTerm.replace('&', '\&\a\m\p\;');
var entry = "<a>" + display_item.replace(new RegExp("(" + temp + ")", 'gi'), '<b>$1</b>');
entry = entry + "</a>";
return q$("<li></li>")
.data("item.autocomplete", item)
.append(entry)
.appendTo(ul);
};
};
var q$ = jQuery.noConflict();
q$(document).ready(function() {
// bind the autocomplete here for execution when the page loads initially
bindAutoComplete();
});
function HtmlDecode(s) {
var out = "";
if (s==null) return;
var l = s.length;
for (var i=0; i<l; i++) {
var ch = s.charAt(i);
if (ch == '&') {
var semicolonIndex = s.indexOf(';', i+1);
if (semicolonIndex > 0) {
var entity = s.substring(i + 1, semicolonIndex);
if (entity.length > 1 && entity.charAt(0) == '#') {
if (entity.charAt(1) == 'x' || entity.charAt(1) == 'X')
ch = String.fromCharCode(eval('0'+entity.substring(1)));
else
ch = String.fromCharCode(eval(entity.substring(1)));
} else {
switch (entity) {
case 'quot': ch = String.fromCharCode(0x0022); break;
case 'amp': ch = String.fromCharCode(0x0026); break;
case 'lt': ch = String.fromCharCode(0x003c); break;
........
case 'clubs': ch = String.fromCharCode(0x2663); break;
case 'hearts': ch = String.fromCharCode(0x2665); break;
case 'diams': ch = String.fromCharCode(0x2666); break;
default: ch = ''; break;
}
}
i = semicolonIndex;
}
}
out += ch;
}
return out;
}
</script>
The command buttons would be defined like this so that when the AJAX POST and rerender are complete, the script that binds the autocomplete is executed again and bound to the elements which are now in the page (after the elements targeted by the rerender have all been replaced).
<apex:pageBlockButtons location="bottom" >
<apex:commandbutton action="{!saveNewRequiredCertificate}" value="{!$Label.Save}" rerender="thePageToRefresh" oncomplete="bindAutoComplete();" />
<apex:commandbutton action="{!CancelNewRequiredCertificate}" value="{!$Label.Cancel}" rerender="thePageToRefresh" oncomplete="bindAutoComplete();" immediate="true"/>
</apex:pageBlockButtons>
Best Answer
Thanks for your response, @Regal. Well, after reading further, I am a dummy! Apparently you have to make your class and method Global! Anyway, for anyone else out there banging their head on the table about this, you can find the answer in the JSRemoting documentation here