Distinct values for SPAutocomplete

Dec 23, 2010 at 2:46 PM

Hello,
I modified the spautocomplete function to include distinct values.  This was needed so that when users were adding a new listitem and were entering a field value, (topic in my case), they could select from a list of previously entered values, or enter a new one. We didnt want to make them go add a new record to a lookupo list every time they wanted to add a new topic, but we wanted to ensure spelling consistancy so we could group by it. Others may find this useful.
Russell Gove

 

   // Provide suggested values from a list for in input column based on characters typed
    $.fn.SPServices.SPAutocomplete = function(options) {

        var opt = $.extend({}, {
            WebURL: "", 						// [Optional] The name of the Web (site) which contains the sourceList
            sourceList: "", 					// The name of the list which contains the values
            sourceColumn: "", 				// The static name of the column which contains the values
            columnName: "", 					// The display name of the column in the form
            CAMLQuery: "", 					// [Optional] For power users, this CAML fragment will be Anded with the default query on the relatedList
            numChars: 0, 					// Wait until this number of characters has been typed before attempting any actions
            ignoreCase: false, 				// If set to true, the function ignores case, if false it looks for an exact match
            distinct: false, 				// If set to true, the function only adds distinct values to the list -- Russell Gove
            slideDownSpeed: "fast", 			// Speed at which the div should slide down when values match (milliseconds or ["fast" | "slow"])
            processingIndicator: "<img src='_layouts/images/REFRESH.GIF'/>", 			// If present, show this while processing
            debug: false						// If true, show error messages; if false, run silent
        }, options);

        // Find the input control for the column and save some of its attributes
        var columnObj = $("input[Title='" + opt.columnName + "']");
        $("input[Title='" + opt.columnName + "']").css("position", "");
        var columnObjId = $(columnObj).attr("ID");
        var columnObjColor = $(columnObj).css("color");

        if (columnObj.html() == null && opt.debug) {
            errBox("SPServices.SPAutocomplete",
				"columnName: " + opt.columnName,
				"Column is not an input control or is not found on page");
            return;
        }

        // Create a div to contain the results
        var containerId = genContainerId("SPAutocomplete", opt.columnName);

        // Add the container for the matching values		
        columnObj.after("<ul id='" + containerId + "' style='display:none;padding:2px;border:1px solid #2A1FAA;background-color:#FFF;position:absolute;z-index:40;margin:0'>");
        // Set the width to match the width of the input control
        $("#" + containerId).css("width", columnObj.width());

        // Wrap the input control in a table
        columnObj.wrap("<table id='" + containerId + "container' cellpadding='0' cellspacing='0' width='100%'><tr><td width='" + columnObj.width() + "'></td></tr></table>");
        // Add a table cell to the right to show the processingIndicator
        columnObj.closest("tr").append("<td id='" + containerId + "processingIndicator' style='display:none;'>" + opt.processingIndicator + "</td>");

        // Handle keypresses
        $(columnObj).keyup(function() {

            // Get the column's value
            var columnValue = $(this).val();

            // Have enough characters been typed yet?
            if (columnValue.length < opt.numChars) {
                $("#" + containerId).hide();
                return false;
            }

            // Hide the container while we're working on it
            $("#" + containerId).hide();

            // Show the the processingIndicator
            $("#" + containerId + "processingIndicator").show();

            // Array to hold the matched values
            var matchArray = new Array();

            // Build the appropriate CAMLQuery
            var camlQuery = "<Query><OrderBy><FieldRef Name='" + opt.sourceColumn + "'/></OrderBy><Where>";
            if (opt.CAMLQuery.length > 0) camlQuery += "<And>";
            camlQuery += "<IsNotNull><FieldRef Name='" + opt.sourceColumn + "'/></IsNotNull>";
            if (opt.CAMLQuery.length > 0) camlQuery += opt.CAMLQuery + "</And>";
            camlQuery += "</Where></Query>";

            // Call GetListItems to find all of the potential values
            $().SPServices({
                operation: "GetListItems",
                async: false,
                webURL: opt.WebURL,
                listName: opt.sourceList,
                // Make sure we get all the items, ignoring any filters on the default view.
                //CAMLQuery: "<Query><Where><IsNotNull><FieldRef Name='" + opt.sourceColumn + "'/></IsNotNull></Where><OrderBy><FieldRef Name='" + opt.sourceColumn + "'/></OrderBy></Query>",
                CAMLQuery: camlQuery,
                CAMLViewFields: "<ViewFields><FieldRef Name='" + opt.sourceColumn + "' /></ViewFields>",
                // Override the default view rowlimit and get all appropriate rows
                CAMLRowLimit: 0,
                completefunc: function(xData, Status) {
                    // Handle upper/lower case if ignoreCase = true
                    var testValue = opt.ignoreCase ? columnValue.toUpperCase() : columnValue;
                    // See which values match and add the ones that do to matchArray
                    $(xData.responseXML).find("[nodeName=z:row]").each(function() {
                        var thisValue = opt.ignoreCase ? $(this).attr("ows_" + opt.sourceColumn).toUpperCase() : $(this).attr("ows_" + opt.sourceColumn);
                        if (testValue == thisValue.substr(0, testValue.length)) {
                            matchArray.push($(this).attr("ows_" + opt.sourceColumn));
                        }
                    });
                }
            });

            // Build out the set of list elements to contain the available values
            var out = "";
            if (opt.distinct == false) {
                for (i = 0; i < matchArray.length; i++) {
                    out += "<li style='display: block; position: relative; cursor: pointer;'>" + matchArray[i] + "</li>";
                }
            }
            else {
                var found = false;
            
                for (i = 0; i < matchArray.length; i++) {
                    // If it appeared previously in the array, dont add again
                    found = false;
                    for (j = 0; j < i; j++) {
                        if (matchArray[i] == matchArray[j]) {
                            found = true;
                            break;
                        }
                    }
                    if (found == false) {
                        out += "<li style='display: block; position: relative; cursor: pointer;'>" + matchArray[i] + "</li>";

                    }
                }
            }

            // Add all the list element to the containerId container
            $("#" + containerId).html(out);
            // Set up hehavior for the available values in the list element
            $("#" + containerId + " li").click(function() {
                $("#" + containerId).fadeOut(opt.slideUpSpeed);
                $("#" + columnObjId).val($(this).html());
            }).mouseover(function() {
                var mouseoverCss = {
                    "cursor": "hand",
                    "color": "#ffffff",
                    "background": "#3399ff"
                };
                $(this).css(mouseoverCss);
            }).mouseout(function() {
                var mouseoutCss = {
                    "cursor": "inherit",
                    "color": columnObjColor,
                    "background": "transparent"
                };
                $(this).css(mouseoutCss);
            });

            // If we've got some values to show, then show 'em!
            $("#" + containerId + "processingIndicator").fadeOut("slow");
            if (matchArray.length > 0) $("#" + containerId).slideDown(opt.slideDownSpeed);
        });

    }; // End of function SPAutocomplete

Dec 23, 2010 at 5:06 PM

Also , I changed it to only return 'matches' from the web service , rather than all values.

  // Provide suggested values from a list for in input column based on characters typed
    $.fn.SPServices.SPAutocomplete = function(options) {

        var opt = $.extend({}, {
            WebURL: "", 						// [Optional] The name of the Web (site) which contains the sourceList
            sourceList: "", 					// The name of the list which contains the values
            sourceColumn: "", 				// The static name of the column which contains the values
            columnName: "", 					// The display name of the column in the form
            CAMLQuery: "", 					// [Optional] For power users, this CAML fragment will be Anded with the default query on the relatedList
            numChars: 0, 					// Wait until this number of characters has been typed before attempting any actions
            ignoreCase: false, 				// If set to true, the function ignores case, if false it looks for an exact match
            distinct: false, 				// If set to true, the function only adds distinct values to the list -- Russell Gove
            slideDownSpeed: "fast", 			// Speed at which the div should slide down when values match (milliseconds or ["fast" | "slow"])
            processingIndicator: "", 			// If present, show this while processing
            debug: false						// If true, show error messages; if false, run silent
        }, options);

        // Find the input control for the column and save some of its attributes
        var columnObj = $("input[Title='" + opt.columnName + "']");
        $("input[Title='" + opt.columnName + "']").css("position", "");
        var columnObjId = $(columnObj).attr("ID");
        var columnObjColor = $(columnObj).css("color");

        if (columnObj.html() == null && opt.debug) {
            errBox("SPServices.SPAutocomplete",
				"columnName: " + opt.columnName,
				"Column is not an input control or is not found on page");
            return;
        }

        // Create a div to contain the results
        var containerId = genContainerId("SPAutocomplete", opt.columnName);

        // Add the container for the matching values		
        columnObj.after("
    "); // Set the width to match the width of the input control $("#" + containerId).css("width", columnObj.width()); // Wrap the input control in a table columnObj.wrap("
     
    "); // Add a table cell to the right to show the processingIndicator columnObj.closest("tr").append(" " + opt.processingIndicator + ""); // Handle keypresses $(columnObj).keyup(function() { // Get the column's value var columnValue = $(this).val(); // Have enough characters been typed yet? if (columnValue.length < opt.numChars) { $("#" + containerId).hide(); return false; } // Hide the container while we're working on it $("#" + containerId).hide(); // Show the the processingIndicator $("#" + containerId + "processingIndicator").show(); // Array to hold the matched values var matchArray = new Array(); // Build the appropriate CAMLQuery //var camlQuery = ""; //if (opt.CAMLQuery.length > 0) camlQuery += ""; //camlQuery += ""; //if (opt.CAMLQuery.length > 0) camlQuery += opt.CAMLQuery + ""; //camlQuery += ""; // Build the appropriate CAMLQuery to only return items that match--Russell gove var camlQuery = ""; if (opt.CAMLQuery.length > 0) camlQuery += ""; camlQuery += "" + $(columnObj)[0].value + ""; if (opt.CAMLQuery.length > 0) camlQuery += opt.CAMLQuery + ""; camlQuery += ""; // Call GetListItems to find all of the potential values $().SPServices({ operation: "GetListItems", async: false, webURL: opt.WebURL, listName: opt.sourceList, // Make sure we get all the items, ignoring any filters on the default view. //CAMLQuery: "", CAMLQuery: camlQuery, CAMLViewFields: "", // Override the default view rowlimit and get all appropriate rows CAMLRowLimit: 0, completefunc: function(xData, Status) { // Handle upper/lower case if ignoreCase = true var testValue = opt.ignoreCase ? columnValue.toUpperCase() : columnValue; // See which values match and add the ones that do to matchArray $(xData.responseXML).find("[nodeName=z:row]").each(function() { var thisValue = opt.ignoreCase ? $(this).attr("ows_" + opt.sourceColumn).toUpperCase() : $(this).attr("ows_" + opt.sourceColumn); if (testValue == thisValue.substr(0, testValue.length)) { matchArray.push($(this).attr("ows_" + opt.sourceColumn)); } }); } }); // Build out the set of list elements to contain the available values var out = ""; if (opt.distinct == false) { for (i = 0; i < matchArray.length; i++) { out += "
  • " + matchArray[i] + ""; } } else { var found = false; for (i = 0; i < matchArray.length; i++) { // If it appeared previously in the array, dont add again found = false; for (j = 0; j < i; j++) { if (matchArray[i] == matchArray[j]) { found = true; break; } } if (found == false) { out += "
  • " + matchArray[i] + ""; } } } // Add all the list element to the containerId container $("#" + containerId).html(out); // Set up hehavior for the available values in the list element $("#" + containerId + " li").click(function() { $("#" + containerId).fadeOut(opt.slideUpSpeed); $("#" + columnObjId).val($(this).html()); }).mouseover(function() { var mouseoverCss = { "cursor": "hand", "color": "#ffffff", "background": "#3399ff" }; $(this).css(mouseoverCss); }).mouseout(function() { var mouseoutCss = { "cursor": "inherit", "color": columnObjColor, "background": "transparent" }; $(this).css(mouseoutCss); }); // If we've got some values to show, then show 'em! $("#" + containerId + "processingIndicator").fadeOut("slow"); if (matchArray.length > 0) $("#" + containerId).slideDown(opt.slideDownSpeed); }); }; // End of function SPAutocomplete
Coordinator
Dec 24, 2010 at 4:34 AM

russgove:

Thanks a lot for kicking these changes back in. I'll take a look through them and add them to the list for the next release.. Based on your description, I'm sure they are a good idea.

M.

Coordinator
Jan 31, 2011 at 3:17 PM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.