I have implemented the jQuery UI autocomplete functionality on a input field using JSLink.
It works like a charm when I test it using Internet Explorer 9, 10, 11 and Chrome.
Some users are however getting a JavaScript error which seems to break the autocomplete functionality, while others who gets it are not bothered at all.
Uncaught TypeError: Cannot read property 'toString' of undefined
Looking at the stacktrace if might have something to do with SP.List.getItemById, but I cannot see how I can do anything different. Does anyone have any ideas?
Here is the most important bits of the code:
// Create function for customizing Field rendering
CourseAutocompleteField.customizeFieldRendering = function () {
var fieldJsLinkOverride = {};
fieldJsLinkOverride.Templates = {};
fieldJsLinkOverride.Templates.Fields = {
// Hook internal field name up with the rendering method
"envi_course": {
"NewForm": CourseAutocompleteField.renderField,
"EditForm": CourseAutocompleteField.renderField
},
};
// Register rendering template
SPClientTemplates.TemplateManager.RegisterTemplateOverrides(fieldJsLinkOverride);
};
// Rendering functions for Course Autocomplete field
CourseAutocompleteField.renderField = function (ctx)
{
// Insert jQuery library
var jq = document.createElement('script');
jq.src = window.location.protocol + "//" + window.location.host + "/_layouts/15/w.e.e/libraries/jquery-ui.min.js";
document.getElementsByTagName('head')[0].appendChild(jq);
// Insert autocomplete stylesheet
var css = document.createElement('link');
css.rel = 'stylesheet';
css.href = window.location.protocol + "//" + window.location.host + "/_layouts/15/w.e.e/css/autocomplete.css";
document.getElementsByTagName('head')[0].appendChild(css);
// Make sure sp.js is loaded
SP.SOD.executeFunc('sp.js', 'SP.ClientContext', CourseAutocompleteField.getCourseValues);
// return input field
var result = SPFieldText_Edit(ctx); // render text field using built-in CSR function
var $f = $(result);
return $f.html();
};
Using SP.SOD.executeFunc I make sure that sp.js is loaded and using the method below I get the data needed for autocomplete.
CourseAutocompleteField.getCourseValues = function () {
try {
var context = new SP.ClientContext.get_current();
var web = context.get_web();
var predefinedCoursesList = web.get_lists().getByTitle('Prædefinerede forløb');
var query = new SP.CamlQuery();
var xml = [];
xml.push('<View>');
xml.push('<Query>');
xml.push('<Where>');
xml.push('<IsNotNull>');
xml.push('<FieldRef Name="envi_course" />');
xml.push('</IsNotNull>');
xml.push('</Where>');
xml.push('<OrderBy>');
xml.push('<FieldRef Name="envi_course" Ascending="False" />');
xml.push('</OrderBy>');
xml.push('</Query>');
xml.push('<ViewFields>');
xml.push('<FieldRef Name="envi_course" />');
xml.push('<FieldRef Name="envi_organizer" />');
xml.push('<FieldRef Name="envi_desc" />');
xml.push('</ViewFields>');
xml.push('</View>');
query.set_viewXml(xml.join(''));
this.courseItems = predefinedCoursesList.getItems(query);
context.load(this.courseItems);
context.executeQueryAsync(
Function.createDelegate(this, CourseAutocompleteField.renderCourseField),
Function.createDelegate(this, CourseAutocompleteField.errorGetCourseValues)
);
}
catch (e) {
console.log(e.message);
}
};
And finally we render it.
CourseAutocompleteField.renderCourseField = function () {
var field = $('input[id^="envi_course"]');
var listEnumerator = this.courseItems.getEnumerator();
while (listEnumerator.moveNext()) {
var item = listEnumerator.get_current();
CourseAutocompleteField.courses.push({
label: item.get_item('envi_course'),
value: item.get_item('envi_course'),
organizer: item.get_item('envi_organizer'),
description: item.get_item('envi_desc')
});
}
field.autocomplete({
minLength: 0,
source: CourseAutocompleteField.courses,
focus: function (event, ui) {
field.val(ui.item.value);
return false;
},
select: function (event, ui) {
field.val(ui.item.value);
$('input[id^="envi_organizer"]').val(ui.item.organizer);
$('textarea[id^="envi_desc"]').val(ui.item.description);
return false;
},
response: function (event, ui) {
return false;
}
}).autocomplete("instance")._renderItem = function (ul, item) {
return $("<li>").append("<a>" + item.value + "</a>").appendTo(ul);
};
};
And it's initiated by calling "CourseAutocompleteField.customizeFieldRendering();"