SPServices Stories #17: Multiple Form Fields Autocomplete for SharePoint 2010/2013 using JavaScript
- SPServices Stories #1 – How to Start a Workflow on Multiple Items in a List
- SPServices Stories #2 – Charting List Data with HighCharts
- SPServices Stories #3 – AddWebPart Method of the WebPartPages Web Service
- SPServices Stories #4 – Using SPServices to Process Files from a Non-Microsoft Source
- SPServices Stories #5 – Gritter and Sharepoint: Integrate Nifty Notifications in Your Intranet!
- SPServices Stories #6 – Custom Quizzing System
- SPServices Stories #7 – Example Uses of SPServices, JavaScript and SharePoint
- SPServices Stories #8 – CEWP, Nintex, jQuery, SPServices and the Client API
- SPServices Stories #9: Developing with Client-side Technologies: jQuery, REST, SPServices and jsRender
- SPServices Stories #10 – jqGrid Implementation Using SPServices in SharePoint
- SPServices Stories #11 – Using SPServices and jQuery to Perform a Redirect from a SharePoint List NewForm to EditForm
- SPServices Stories #12 – SharePoint and jQuery SPServices in Education: A Case Study
- SPServices Stories #13: Durandal SP3: Developing SharePoint SPAs Made Easy
- SPServices Stories #14: Create a Slide Show in SharePoint 2010 using an Announcements List and SPServices
- SPServices Stories #15: Custom Client-side Calendar Query for Office 365 SharePoint Site Using SPServices jQuery Library
- SPServices Stories #17: Multiple Form Fields Autocomplete for SharePoint 2010/2013 using JavaScript
- SPServices Stories #18 – Retrieve Managed Metadata Using JavaScript and SPServices
- SPServices Stories #19 – Folders in SharePoint are as necessary as evil. Make the best of it using jQuery and SPServices.
- SPServices Stories #20 – Modify User Profile Properties on SharePoint Online 2013 using SPServices
- SPServices Stories #22 : SPServices SharePoint Attachments in Internet Explorer 9
- SPServices Stories #21 – Redirect If User Clicked a Button Previously
Introduction
Anyone who follows this series knows that I’m always on the lookout for interesting and useful implementations of SPServices. A few weeks ago, a tweet in my feeds caught my eye:
Just posted multiple fields autocomplete jQuery plugin using @sympmarc's SPServices http://t.co/Z8WJSDVakQ
— Plumsail (@plumsail_sp) August 21, 2013
Anton Khritonenkov, who is a Technical Lead at Plumsail in Russia, has come up with a nice way to enable autocomplete behavior for multiple list columns based on related lists.
While I’ve got the SPAutocomplete function in SPServices, it’s pretty rudimentary and I usually suggest using the jQueryUI autocomplete function, as Anton does here. The other function in SPServices that is somewhat related to what Anton is doing in SPDisplayRelatedInfo. By using the completefunc there, you could enable some of the behavior that Anton has built, but not as cleanly.
Anton explains things well in his post below, which he was kind enough to allow me to repost here from his original on the CodeProject site.
Multiple Form Fields Autocomplete for SharePoint 2010/2013 using JavaScript
Introduction
Filling forms could be painful sometimes, especially for forms with many fields. In this article I’ll describe approach which can simplify forms filling significantly. In my case I needed to implement order form interface. In fact it contains a lot of fields I can pre-fill based on chosen customer, such as address, ZIP code and account number. All of this data is already stored in separate list called “Customers directory”.
Final requirements could looks like this:
When user starts typing in the customer field, I need to suggest list of customer names from Customers directory. When user chooses customer from suggestions list, I need to read data multiple field values from Customers directory and fill corresponding fields in the order form. User can correct filled values later if needed.
To implement such functionality I used to use JavaScript and this case is not an exception. There are many reasons for this:
- It well fits Office 365 restrictions.
- It easily migrates from older SharePoint versions.
- It is easily to debug without slow SharePoint solution deployment.
- REST SharePoint 2013 API or SPServices are almost such powerful as server code.
- Finally I just like it.
In this article I’ll use SPServices jQuery plugin for communication with SharePoint services. It works for SharePoint 2013 as well as for SharePoint 2010. I’ll also use jQuery UI Autocomplete plugin to implement suggestions functionality.
plumFormAutocomplete plugin works well for single field as much as for multiple fields. Plugin supports text fields only.
Plugin in action looks like this:
How to use plumFormAutocomplete jQuery plugin
You can use this plugin without looking into the code. Firstly, I’ll describe how to use it, then if you still will be interested, you can look inside plugin implementation.
Prerequisites
If you don’t have jQuery, jQuery UI or SPServices, you can download fresh version from official sites. For tutorial purposes I assume that names of downloaded files and folders are following:
- jquery.min.js
- jquery.SPervices.min.js
- jquery-ui-1.10.3.custom.min.js
- jquery-ui-1.10.3.custom.min.css
- css folder for jQuery UI
I also assume that you have jquery.plumFormAutocomplete.js downloaded from source code of this article (Source code download link).
Upload jquery.min.js, jquery.SPServices.min.js, jquery-ui-1.10.3.custom.min.js and jquery.plumFormAutocomplete.js files to Style Library within your site collection. You also need to upload jQuery UI CSS styles located in the same folder with jquery-ui-1.10.3.custom.min.js. Then open New or Edit form, where autocomplete plugin will be used and add js and CSS links to placeholder PlaceHolderAdditionalPageHead. You can use following snippet:
Configure and call plugin
Now you are ready to configure and call plugin. For my case plugin call looks like this:
//get control for autocomplete field var fieldControl = $.getFieldControl('Title'); //call autocomplete plugin for field control fieldControl.plumFormAutocomplete({ sourceList: 'Customers directory', sourceMatchField: 'Title', labelFields: ['Title', 'ZIPCode'], labelSeparator: ', ', fillConcatenatedLabel: false, fieldsMapping: [{sourceField: 'Address', targetField: 'CustAddress'}, {sourceField: 'AccountNumber', targetField: 'CustAccountNumber'}, {sourceField: 'ZIPCode', targetField: 'CustZIPCode'}] });
You can wrap plugin call inside jQuery $(document).ready() function to ensure that code will be executed after page is loaded.
Let us look at this code sample in more detail. Code is divided into two parts:
- Get control for autocomplete field
- Call autocomplete plugin for field control
For the first step you need to specify internal name of autocomplete field for getFieldControl function. It is ‘Title’ in my case.
In the second step you need to call plugin for received autocomplete field and configure plugin options. Plugin options are structured as a single object as any jQuery plugin options.
Plugin options
- sourceList – name or GUID of source list, where suggestions will be taken. It is ‘Customers directory’ in my case.
- sourceMatchField – internal name of the field in the source list. This field will be used to find matching list items for autocomplete keywords.
- labelFields – an optional parameter, you can specify source list field internal names array. All field values for these fields will be concatenated with labelSeparator and displayed in autocomplete suggestion as a single string like this: Value1, Value2, …, ValueN.
- labelSeparator – an optional parameter, it is separator for labelFields concatenation, for example it could be comma with space (‘, ‘).
- fillConcatenatedLabel – an optional parameter, set true if you need to fill autocomplete textbox with all concatenated labelFields values, set false if you need to fill autocomplete text box only with single field value.
- fieldsMapping – an optional parameter, it is an array of field mapping objects. Each object declares mapping from source list field to target list field. In my case names of source and target fields are the same. For example Address in Customer directory and Address in Orders list.
Mapping object has following syntax:
{sourceField: 'Internal name of source field', targetField: 'Internal name of target field'}
Note: You can specify only non optional parameters, plugin will work correctly. This plugin works well as single field autocomplete too, just do not fill optional parameters.
Plugin configuration without optional parameters could look like this:
fieldControl.plumFormAutocomplete({ sourceList: 'Customers directory', sourceMatchField: 'Title' });
Internal implementation of the plugin
Let us look at the full plugin source code. You can download it here.
There are three major part inside the code:
- Getting text field input.
- Apply jQueryUIi autocomplete plugin and SPServices to get suggestions.
- Implementing helper functions.
To get field input I used jQuery selectors and simple regular expression. Unfortunately SharePoint doesn’t provide any method to get field controls from JavaScript, it only stores field internal name inside html comment in the following format:
<!-- FieldName="Title" FieldInternalName="Title" FieldType="SPFieldText" -->
So, I had to parse it to find control I needed. Final function was added to jQuery:
//function gets text field control by internal name $.getFieldControl = function (fieldInternalName) { var regexStr = 'FieldInternalName="' + fieldInternalName + '"' var regex = new RegExp(regexStr, 'i'); var fieldCell = $('td.ms-formbody').filter(function () { return (regex).test($(this).html()) }); return $(fieldCell.find('input')[0]); }
In the next step I applied jQuery UI autocomplete plugin and implemented source and select plugin functions. Source function calls source list using SPServices and CAML to get suggestions. When suggestion found, I store all mapped field values inside autcomplete object:
source: function (request, response) { var autocompleteVals = []; var k = 0; $().SPServices({ operation: "GetListItems", async: false, listName: options.sourceList, CAMLViewFields: getViewFields(options.fieldsMapping), CAMLQuery: getCamlQuery(options.sourceMatchField, request.term), completefunc: function (xData, Status) { $(xData.responseXML).SPFilterNode("z:row").each(function () { var queryResult = this; var fieldsValues = getFieldsValues(options.fieldsMapping, queryResult); var labelText = getLabelText(fieldsValues); autocompleteVals[k] = { label: labelText, value: options.fillConcatenatedLabel ? labelText : extractFieldValue(fieldsValues, options.sourceMatchField), fieldsValues: fieldsValues }; k++; function getLabelText(fieldValues){ var result = ''; if(options.labelFields) { for(i = 0; i < options.labelFields.length; i++) { var fieldName = options.labelFields[i]; var fieldVal = extractFieldValue(fieldValues, fieldName); if(fieldVal != '') { if(i > 0) { result += options.labelSeparator; } result += fieldVal; } } } else { result += extractFieldValue(fieldValues, options.sourceMatchField); } return result; } }); response(autocompleteVals); } }); }
Select function fills values inside mapped fields according to matched item from source lists. It reads values stored inside ui.item and fills corresponding fields based on suggestion selection.
select: function (event, ui) { //Fill all depended fields $.each(ui.item.fieldsValues, function () { var fieldVal = this; var fieldInput = $.getFieldControl(fieldVal.key); var outputVal = fieldVal.value; if (outputVal) { var lookupSeparator = ';#'; if (outputVal.indexOf(lookupSeparator) != -1) { var ind = outputVal.indexOf(lookupSeparator); var length = lookupSeparator.length; var startInd = ind + length; outputVal = outputVal.substring(startInd, outputVal.lenght) } fieldInput.val(outputVal); } }); }
Maybe you discovered that there are three helper functions inside plugin: getFieldsValues
, getViewFields
, and getCamlQuery
.
getFieldsValues
parses SPServices response and fills autocomplete object according to specified fields mapping.
//get values for all mapped fields function getFieldsValues(fieldsMapping, queryResult) { var result = []; $.each(fieldsMapping, function () { var fieldMapping = this; var val = $(queryResult).attr("ows_" + fieldMapping.sourceField); result.push({ key: fieldMapping.targetField, value: val, sourceKey: fieldMapping.sourceField}); }); var sourceVal = $(queryResult).attr("ows_" + options.sourceMatchField); result.push({ value: sourceVal , sourceKey: options.sourceMatchField}); return result; }
getViewFields
generates ViewFields xml for CAML query according to fields mapping.
//get view fields for all mapped fields function getViewFields(fieldsMapping) { var result = ""; var isSourceFieldAdded = false; if(fieldsMapping){ $.each(fieldsMapping, function () { var mapping = this; var viewField = ""; result += viewField; }); isSourceFieldAdded = fieldsMapping.filter(function(){ return this.sourceField == options.sourceMatchField; }).length > 0; } if(!isSourceFieldAdded){ result += ""; } result += ""; return result; }
getCamlQuery
generates CAML query according to filter column internal name and keyword from input.
//get CAML query for keyword function getCamlQuery(colname, keyword) { var where = "" + keyword + ""; var orderBy = ""; var query = "" + where + orderBy + ""; return query; }
Updates
03.10.2013
- Additional optional parameters added:
labelFields
,labelSeparator
,fillConcatenatedLabel
. - Reading lookup fields from source list implemented.
- Minor bug fixes.
This does not work with SharePoint Foundation :(
Hi Sir,
Am new to Sp Services .I need a small help.
I have a SharePoint 2010 custom list.Which is having a SPD workflow.The list is updating quite a long time.So need to show a status bar/Gear.gif on top of page while list is loading.
If workflow variable status is blank or In progress at that time only i need to show.
Thank you very much.
Srinivasa:
Workflows run in a timer job asynchronously, so they don’t have anything to do with any views loading slowly. I’d suggest rethinking the views a bit, as you may simply be displaying too much content.
M.
i am trying to use this with a custom list new form in sharepoint foundation 2010 but nothing is happening
This is quite an old post, with a bunch of moving parts. As with most examples like this, you’ll need to adapt it to your needs and do some debugging.
M.
Is there a way to make this work so the autocomplete spans 5 lists — not just one?