SPCascadeDropdowns() - Not using UID's on Query to populate child column

Sep 28, 2011 at 8:58 PM
Edited Sep 28, 2011 at 11:19 PM

I have uncovered a problem with the data populated on a child column for which I don't have a good work-around for... perhaps someone can help...  Ultimately the issue is around the fact that more specific filtering criteria is needed on each call to the server for gathering data to populate the child column.

DETAIL:

I have a page that has multiple cascade elements on the page... the field are:

Product -> Releases -> Impacted by Features

They are all linked... changing Product, populates Releases; changing releases populates Features...

The List I have setup to support this are:

  • Products
  • Releases - Contains a column labeled 'Product' which is a LookupField to the Products Table
  • Features - Contains a column labeled 'Product' which is a lookupfield to the products table, as well as a column labeled 'Release' which is a lookupfield to the Releases table.

 

The problem happens when populating the list of features... Because the query to SP is done on the Release number (not using the UID), the response will include all rows that have the "visible" release number, which is a problem if the same release number is defined for multiple products.

I have been pondering how to address this... The only thing I can think of that would help, given the current version of this utility, would be to "detach" the SPCascadeDropdowns() from the 'Impacted by Features' and re-attach it every-time Release's changes, given the definition additional filters consisting of the currently selected product and currently selected release.  My question is: how do I "detach"/"remove" SPCascadeDropdowns() from a field?

 

Some Possible Solutions (Enhancements)

I did try to define the additional filter criteria (product=x) on the definition of the SPCascadeDropdowns() for the features using the 'CAMLQuery' parameter.  This does not work because once defined the first time, the filter criteria is used for all subsequent calls... meaning if I pick a different Product, the call to populate the feature will still have the initial filter definition.
PROPOSE: Enhance SPCascadeDropdowns() to allow for additional filtering criteria to be defined on each Parent column change (ex: a new parameter called onPreQueryCAML)

I also contemplated using the oncomplete parameter to define a function that would filter out the field after the fact... however, i would have to create code that covers the many aspects of how SP displays lists with lookupfields.  This is also not optimal from a performance standpoint, as I would be making additional calls to the server to retrieve the correct data... adding additional delay for "giving" the GUI back to the user...

Another approach would be for SPCascadeDropdowns() to use the UID instead of the visual value on the query... the call to the List would use the following:

 

<Eq>
     <FieldRef Name="Release" LookupId="TRUE" />
     <Value Type="Lookup">10</Value>
</Eq>

 

(note the LookupId attribute above on the fieldRef).... Instead of the existing call:

 

<Eq>
     <FieldRef Name="Release" />
     <Value Type="Lookup">7.3.0.05</Value>
</Eq>

 

 

Thank you all for your help.

Paul T.

Sep 28, 2011 at 11:12 PM

When using Lookups, I use the LookupId='True' attribute 99% of the time.  It's hassle free and guaranteed to be unique.  It's also pretty simple to get the value of a drop down which is the ID of the list item any way.  Would changing your query to use that work for you?

Cheers,

Matt

Sep 28, 2011 at 11:18 PM

Matt,

Thanks for your response...

Yes, using LookupId='true' would be the optimal solution... but I'm not the one building the query... SPCascadeDropdowns() is method that builds it and thus why I posted this thread... I did not want to fork the code for SPCascadeDropdowns() and create my own version but rather am looking to see if I can play with what the utility provides out of the box.

Paul.

Sep 29, 2011 at 12:21 AM

Gotcha Paul,

Seems like this function should already be doing this.  I wonder what Marc has to say...

Good work in tracking all of this info down Paul.  You definitely did a thorough job. 

Cheers,

Matt

Sep 29, 2011 at 1:02 AM

Thanks Matt...

Marc may have had a good reason for this... One, that I can think of, is if the column is not defined as a lookupfield in the List...  But even if that is the case, it looks like he already pulls in the List definition as part of SPCascadeDropdowns(), thus he has this information available and can store it for use when building the CAML query.

Oct 12, 2011 at 12:02 AM

Marc,

Any feedback on using LookupId on the query?  I will make the change to my local copy of SPServices, but wanted to get your feedback first, specially around other potential side affects (if any).

Thanks.

Paul.

Coordinator
Oct 12, 2011 at 12:07 AM

Paul:

Sorry I haven't gotten back to you on this.

My intent with SPCascadeDropdowns is for the underlying lists to be relationally "pure", meaning that no value will occur more than once. SharePoint itself doesn't deal well with situations where there are multiple, matching values in a lookup list.
http://sympmarc.com/2011/05/23/obscure-and-not-so-obscure-bugs-with-complex-dropdowns-in-sharepoint-forms/

The other point is that to the user, duplicate values look exactly the same, so they don't know which one to select.

M.

Oct 12, 2011 at 2:47 AM

Mark,

Thanks for the reply... I'm hopping you will entertain a little more discussion on this..

The post you reference above was a good one... I can certainly understand why you "...introduce the same bug into SPServices so that I can mimic the out of the box functionality.", but I still question whether that is the right approach to take for SPServices... after all, you are providing a better way for an IT Person or developer to enhance the UI beyond what SP provides out of the box. This should mean not only provide enhanced functionality, but "get around" SP bugs and/or quirks.

The Section titled "Wrong selection", in your post describes the issue/bug that Sharepoint makes visible, however, the one I describe above highlights the one that SPServices is causing (in my opinion :) ) by populating the "child" column... Without SPCascadeDropdowns() applied, the child field presents all values (duplicates Titles included)... The assumption would be that the use of the utility will help manage/restrict the selection by the user to a "logical" value; a value whose "unique" parent column is the same as the one selected by the user in the Parent Column.  Uniqueness, as you have highlighted in your post, can only be achieved by using the UID of the parent column).  This uniqueness does not insure that the child column values are all unique, but rather that all of those rows are associated with the same parent records/row.

It just seems that using the "lookupId=True" attribute in the query that populates the child column is the most appropriate. This will insure that the True correct rows are returned.   The other possible way, if you really wanted to preserve the SP Bug, would be to maybe introduce an additional input parameter to SPCascadeDropdowns() that would trigger the use of the lookupId=true attribute in the query.   That could allow this utility to continue to meet your initial intent while providing us with a option to be more specific on how the child values are retrieved.

In the short-term, I'm going to dive into SPServices code and see if I can identify the method I can override or where I can make the change to use the lookupId... I currently have this issue happening in one production deployment, and thus need to get around it (its causing logical "corruption")...

Thanks again for your time and attention.. Really appreciate it.

 

Paul.

Nov 21, 2011 at 5:16 PM
Edited Dec 21, 2011 at 10:18 PM

SPSevices Community,

Wanted to post back here the solution I have implemented to address this problem... I have (unwilling :) ) changed the SPServices code (v.0.6.2) and thus introduced my own local fork of the library... To re-iterate, the problem I'm trying to fix is:

 

Enhance SPCascadeDropdowns() to use the ID instead of the visual value (title) on the query to Sharepoint webservices

 

The resolution was actually simple (I hope I did not miss something)... I added a new input parameter to the utility called 'useLookupId' of type Boolean and set it default value to true (this met my immediate need to always use LookupId and not have to redefined all the calls I have defined in my application. Should Marc ever pickup this enh. to the core plugin, I can see this value having a default of False, to insure original SP behavior is maintained.).  When set to True, the utility will use ID's instead of TITLE's in the query to SharePoint, and thus insuring that the exact filter the user is looking for will be used.

In addition, while looking at the code, I noticed 2 server calls to get List information that were (in my opinion) unnecessary; these were being made each time the user selected a different value from the ParentColumn to determine the Static name of the ParentColumn and to see if the ChildColumn is a Required column. Because a List definition is unlikely to change (all that often) I did not see the need to constantly pull the server, when the values can just be cached after the first time we retrieve them.

The changes I made are summarized as follows:

 $.fn.SPServices.SPCascadeDropdowns (starting at line 1254)

  • Changed input options to include new parameter called 'useLookupId' and set its default value to true

function cascadeDropdown() (starting at line 1335)

  • Changed to retrieval of field values to check if useLookupId parameter is true, and if so, then retrieve the selected element's ID from the parent column
  • Changed the definition of the query (variable camlQuery) to check if useLookupId is true and if so, use the LookupId=true in the definition of the <fieldRef...>
  • Cached parent column Static name
  • Cached if child Column is required

 

The actual changed code is below (there was no way to upload the changed file)... I can also provide a DIFF (from eclipse) if needed or post the changed file somewhere.

Change to SPCascadeDropdowns:

(note: showing only the first few lines of this method)


 

    // Function to set up cascading dropdowns on a SharePoint form
    // (Newform.aspx, EditForm.aspx, or any other customized form.)
    $.fn.SPServices.SPCascadeDropdowns = function(options) {

        var opt = $.extend({}, {
            relationshipWebURL: "",             // [Optional] The name of the Web (site) which contains the relationships list
            relationshipList: "",               // The name of the list which contains the parent/child relationships
            relationshipListParentColumn: "",   // The internal name of the parent column in the relationship list
            relationshipListChildColumn: "",    // The internal name of the child column in the relationship list
            relationshipListSortColumn: "",     // [Optional] If specified, sort the options in the dropdown by this column,
                                                // otherwise the options are sorted by relationshipListChildColumn
            parentColumn: "",                   // The display name of the parent column in the form
            childColumn: "",                    // The display name of the child column in the form
            listName: $().SPServices.SPListNameFromUrl(),       // The list the form is working with. This is useful if the form is not in the list context.
            CAMLQuery: "",                      // [Optional] For power users, this CAML fragment will be Anded with the default query on the relatedList
            promptText: "Choose {0}...",        // [Optional] Text to use as prompt. If included, {0} will be replaced with the value of childColumn
            noneText: "(None)",                 // [Optional] Text to use for the (None) selection. Provided for non-English language support.
            simpleChild: false,                 // [Optional] If set to true and childColumn is a complex dropdown, convert it to a simple dropdown
            selectSingleOption: false,          // [Optional] If set to true and there is only a single child option, select it
            completefunc: null,                 // Function to call on completion of rendering the change.
            debug: false,                       // If true, show error messages;if false, run silent
            useLookupId: true                   // PT: [optional] If set, then the query to SP will use the ID's of the parent column rather than the visible title.
        }, options);

......

 

Changes to cascadeDropdown()

11/28/11: Edited code below based on thread discussion here; included better method of retrieving ID of a complex field.

12/21/11: Updated again to insure that when using LookupId=TRUE, the <value> element has a type of Integer.

 

function cascadeDropdown(opt, childSelect) {
    var choices = "";
    var childSelectSelected = null;
    var parentSelectSelected = [];
    var master;
    var MultiLookupPickerdata;
    var newMultiLookupPickerdata;
    var childColumnRequired;
    var numChildOptions;
    var firstChildOptionId;
    var firstChildOptionValue;

    // Find the parent column's select (dropdown)
    var parentSelect = new dropdownCtl(opt.parentColumn);
    // Get the parent column selection(s)
    switch(parentSelect.Type) {
        case "S":
            // PT: get value if useLookupId is true 
            if (opt.useLookupId) {
                parentSelectSelected.push(parentSelect.Obj.find("option:selected").val() || "");                    
            } else {
                parentSelectSelected.push(parentSelect.Obj.find("option:selected").text());
            }
            break;
        case "C":
            // PT: get value if useLookupId is true
            if (opt.useLookupId) {
                // PT: get possible matches from the attribute CHOICES and then find
                // the selected item's id
                // Get it first by using the hidden element... if not found there, then loop
                // through the CHOICES and get first one found.
                var eleId = $("input[id='"+ parentSelect.Obj.attr("optHid") + "']").val() || "";
                if (!eleId) {                        
                    var match   = parentSelect.Obj.val();
                    var choices = $(parentSelect.Obj).attr("choices").split("|");
                    var total   = choices.length;
                    for (var p=0;p<total;p=p+2){
                        if (choices[p] == match) {
                            eleId = choices[p+1];
                            break;
                        }
                    }
                }
                parentSelectSelected.push(eleId);
                
            } else {
                parentSelectSelected.push(parentSelect.Obj.attr("value"));
            }
            break;
        case "M":
            parentSelections = parentSelect.Obj.closest("span").find("select[ID$='SelectResult'][Title^='" + opt.parentColumn + " ']");
            $(parentSelections).find("option").each(function() {
                // PT: get value if useLookupId is true
                if (opt.useLookupId) {
                    parentSelectSelected.push($(this).val() || "");
                } else {
                    parentSelectSelected.push($(this).html());
                }
            });
            break;
        default:
            break;
    }

    // If the selection hasn't changed, then there's nothing to do right now.  This is useful to reduce
    // the number of Web Service calls when the parentSelect.Type = "C" or "M", as there are multiple propertychanges
    // which don't require any action.  The attribute will be unique per child column in case there are
    // multiple children for a given parent.
    // PT: for performance reasons, get the ChildCOlumnStatic only when we don't find it
    //      chached. 
    var childColumnStatic = $(childSelect.Obj).data("SPServicesChildColumnStatic");
    if (childColumnStatic === undefined) {
        childColumnStatic = $().SPServices.SPGetStaticFromDisplay({
            listName: opt.listName,
            columnDisplayName: opt.childColumn
        });
        $(childSelect.Obj).data("SPServicesChildColumnStatic", childColumnStatic);
    }
    if(parentSelect.Obj.attr("SPCascadeDropdown_Selected_" + childColumnStatic) === parentSelectSelected.join(";#")) {
        return;
    }
    parentSelect.Obj.attr("SPCascadeDropdown_Selected_" + childColumnStatic, parentSelectSelected.join(";#"));

    // Get the current child column selection, if there is one
    switch(childSelect.Type) {
        case "S":
            childSelectSelected = childSelect.Obj.find("option:selected").val();
            break;
        case "C":
            childSelectSelected = childSelect.Obj.attr("value");
            break;
        case "M":
            MultiLookupPickerdata = childSelect.Obj.closest("span").find("input[name$='MultiLookupPicker$data']");
            master = window[childSelect.Obj.closest("tr").find("button[id$='AddButton']").attr("id").replace(/AddButton/,'MultiLookupPicker_m')];
            currentSelection = childSelect.Obj.closest("span").find("select[ID$='SelectResult'][Title^='" + opt.childColumn + " ']");
            // Clear the master
            master.data = "";
            break;
        default:
            break;
    }

    // When the parent column's selected option changes, get the matching items from the relationship list
    // Get the list items which match the current selection
    var sortColumn = (opt.relationshipListSortColumn.length > 0) ? opt.relationshipListSortColumn : opt.relationshipListChildColumn;
    var camlQuery = "<Query><OrderBy><FieldRef Name='" + sortColumn + "'/></OrderBy><Where>";
    if(opt.CAMLQuery.length > 0) {
        camlQuery += "<And>";
    }

    // Build up the criteria for inclusion
    if(parentSelectSelected.length === 0) {
        // Handle the case where no values are selected in multi-selects
        camlQuery += "<Eq><FieldRef Name='" + opt.relationshipListParentColumn + "'/><Value Type='Text'></Value></Eq>";
    } else if(parentSelectSelected.length === 1) {
        // Only one value is selected
        // PT: get value if useLookupId is true
        camlQuery += "<Eq><FieldRef Name='"
                    +   opt.relationshipListParentColumn + "'"
                    +   (opt.useLookupId ? " LookupId='True'" : "")
                    +   "/><Value "
                    +   (opt.useLookupId ? "Type='Integer'>" : "Type='Text'>")
                    +   escapeColumnValue(parentSelectSelected[0]) + "</Value></Eq>";
    } else {
        var compound = (parentSelectSelected.length > 2) ? true : false;
        for(i=0; i < (parentSelectSelected.length - 1); i++) {
            camlQuery += "<Or>";
        }
        for(i=0; i < parentSelectSelected.length; i++) {
            // PT: get value if useLookupId is true
            camlQuery += "<Eq><FieldRef Name='" + opt.relationshipListParentColumn + "'"
                    +   (opt.useLookupId ? " LookupId='True'" : "")
                    +   "/><Value "
                    +   (opt.useLookupId ? "Type='Integer'>" : "Type='Text'>") 
                    +   escapeColumnValue(parentSelectSelected[i]) + "</Value></Eq>";
            if(i>0 && (i < (parentSelectSelected.length - 1)) && compound) {
                camlQuery += "</Or>";
            }
        }
        camlQuery += "</Or>";
    }

    if(opt.CAMLQuery.length > 0) {
        camlQuery += opt.CAMLQuery + "</And>";
    }
    camlQuery += "</Where></Query>";

    // Get information about the childColumn from the current list
    //PT: Performance improvement. We get the information only if we
    //      don't yet have it. Once we get it the first time, we store
    //      it on the field for future calls to use.
    childColumnRequired = $(childSelect.Obj).data("SPServicesChildColumnRequired");
    if (childColumnRequired === undefined){
        $().SPServices({
            operation: "GetList",
            async: false,
            listName: opt.listName,
            completefunc: function(xData, Status) {
                $(xData.responseXML).find("Fields").each(function() {
                    $(this).find("Field[DisplayName='" + opt.childColumn + "']").each(function() {
                        // Determine whether childColumn is Required
                        childColumnRequired = ($(this).attr("Required") === "TRUE") ? true : false;
                        // Store (cache) value
                        $(childSelect.Obj).data("SPServicesChildColumnRequired", childColumnRequired);
                        // Stop looking; we're done
                        return false;
                    });
                });
            }
        });
    }
    
    $().SPServices({
        operation: "GetListItems",
        // Force sync so that we have the right values for the child column onchange trigger
        async: false,
        webURL: opt.relationshipWebURL,
        listName: opt.relationshipList,
        // Filter based on the currently selected parent column's value
        CAMLQuery: camlQuery,
        // Only get the parent and child columns
        CAMLViewFields: "<ViewFields><FieldRef Name='" + opt.relationshipListParentColumn + "' /><FieldRef Name='" + opt.relationshipListChildColumn + "' /></ViewFields>",
        // Override the default view rowlimit and get all appropriate rows
        CAMLRowLimit: 0,
        // Even though setting IncludeMandatoryColumns to FALSE doesn't work as the docs describe, it fixes a bug in GetListItems with mandatory multi-selects
        CAMLQueryOptions: "<QueryOptions><IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns></QueryOptions>",
        completefunc: function(xData, Status) {
            $(xData.responseXML).find("faultcode").each(function() {
                if(opt.debug) {
                    errBox("SPServices.SPCascadeDropdowns",
                        "relationshipListParentColumn: " + opt.relationshipListParentColumn + " or " +
                        "relationshipListChildColumn: " + opt.relationshipListChildColumn,
                        "Not found in relationshipList " + opt.relationshipList);
                }
                return;
            });

            // Add an explanatory prompt
            switch(childSelect.Type) {
                case "S":
                    // Remove all of the existing options
                    $(childSelect.Obj).find("option").remove();
                    // If the column is required or the promptText option is empty, don't add the prompt text
                    if(!childColumnRequired && (opt.promptText.length > 0)) {
                        childSelect.Obj.append("<option value='0'>" + opt.promptText.replace(/\{0\}/g, opt.childColumn) + "</option>");
                    }
                    break;
                case "C":
                    // If the column is required, don't add the "(None)" option
                    choices = childColumnRequired ? "" : opt.noneText + "|0";
                    childSelect.Obj.attr("value", "");
                    break;
                case "M":
                    // Remove all of the existing options
                    $(childSelect.Obj).find("option").remove();
                    newMultiLookupPickerdata = "";
                    break;
                default:
                    break;
            }
            // Get the count of items returned and save it so that we can select if it's a single option 
            // The item count is stored thus: <rs:data ItemCount="1">
            numChildOptions = parseFloat($(xData.responseXML).find("[nodeName='rs:data']").attr("ItemCount"));

            // Add an option for each child item
            $(xData.responseXML).find("[nodeName='z:row']").each(function() {
                
                // If relationshipListChildColumn is a Lookup column, then the ID should be for the Lookup value,
                // else the ID of the relationshipList item
                var thisOptionId = ($(this).attr("ows_" + opt.relationshipListChildColumn).indexOf(";#") > 0) ?
                        $(this).attr("ows_" + opt.relationshipListChildColumn).split(";#")[0] :
                        $(this).attr("ows_ID");
                // If the relationshipListChildColumn is a calculated column, then the value isn't preceded by the ID,
                // but by the datatype.  In this case, thisOptionId should be the ID of the relationshipList item.
                if(isNaN(thisOptionId)) {
                    thisOptionId = $(this).attr("ows_ID");
                }
                
                // If relationshipListChildColumn is a Lookup column, then strip off the leading ID;# on the value
                var thisOptionValue = ($(this).attr("ows_" + opt.relationshipListChildColumn).indexOf(";#") > 0) ?
                        $(this).attr("ows_" + opt.relationshipListChildColumn).split(";#")[1] :
                        $(this).attr("ows_" + opt.relationshipListChildColumn);
                
                // Save the id and value for the first child option in case we need to select it (selectSingleOption option is true)
                firstChildOptionId = thisOptionId;
                firstChildOptionValue = thisOptionValue;
                
                switch(childSelect.Type) {
                    case "S":
                        var selected = ($(this).attr("ows_ID") === childSelectSelected) ? " selected='selected'" : "";
                        childSelect.Obj.append("<option" + selected + " value='" + thisOptionId + "'>" + thisOptionValue + "</option>");
                        break;
                    case "C":
                        if (thisOptionValue === childSelectSelected) {
                            childSelect.Obj.attr("value", childSelectSelected);
                        }
                        choices = choices + ((choices.length > 0) ? "|" : "") + thisOptionValue + "|" + thisOptionId;
                        break;
                    case "M":
                        childSelect.Obj.append("<option value='" + thisOptionId + "'>" + thisOptionValue + "</option>");
                        newMultiLookupPickerdata += thisOptionId + "|t" + thisOptionValue + "|t |t |t";
                        break;
                    default:
                        break;
                }
            });

            switch(childSelect.Type) {
                case "S":
                    childSelect.Obj.trigger("change");
                    // If there is only one option and the selectSingleOption option is true, then select it
                    if(numChildOptions === 1 && opt.selectSingleOption === true) {
                        $(childSelect.Obj).find("option[value!='0']:first").attr("selected", "selected");
                    }
                    break;
                case "C":
                    // Set the allowable choices
                    childSelect.Obj.attr("choices", choices);
                    // If there is only one option and the selectSingleOption option is true, then select it
                    if(numChildOptions === 1 && opt.selectSingleOption === true) {
                        // Set the input element value
                        $(childSelect.Obj).attr("value", firstChildOptionValue);
                        // Set the vlue of the optHid input element
                        $("input[id='" + childSelect.Obj.attr("optHid") + "']").val(firstChildOptionId);
                    }
                    // Trigger a change so that SharePoint's scripts will fire on the events
                    childSelect.Obj.trigger("propertychange");
                    // If there's no selection, then remove the value in the associated hidden input element (optHid)
                    if(childSelect.Obj.val() === "") $("input[id='" + childSelect.Obj.attr("optHid") + "']").val("");
                    break;
                case "M":
                    MultiLookupPickerdata.attr("value", newMultiLookupPickerdata);
                    // Clear any prior selections that are no longer valid
                    $(currentSelection).find("option").each(function() {
                        var thisSelected = $(this);
                        $(this).attr("selected", "selected");
                        $(childSelect.Obj).find("option").each(function() {
                            if($(this).html() === thisSelected.html()) {
                                thisSelected.attr("selected", "");
                            }
                        });
                    });
                    GipRemoveSelectedItems(master);
                    // Hide any options in the candidate list which are already selected
                    $(childSelect.Obj).find("option").each(function() {
                        var thisSelected = $(this);
                        $(currentSelection).find("option").each(function() {
                            if($(this).html() === thisSelected.html()) {
                                thisSelected.remove();
                            }
                        });
                    });
                    GipAddSelectedItems(master);
                    // Set master.data to the newly allowable values
                    master.data = GipGetGroupData(newMultiLookupPickerdata);

                    // Trigger a dblclick so that the child will be cascaded if it is a multiselect.
                    childSelect.Obj.trigger("dblclick");

                    break;
                default:
                    break;
            }
        }
    });
    // If present, call completefunc when all else is done
    if(opt.completefunc !== null) {
        opt.completefunc();
    }
} // End cascadeDropdown

 

Hope others find this useful...

Paul T.

Coordinator
Nov 24, 2011 at 3:43 PM

Paul:

I appreciate your work on this. Truly. I wanted to get the v0.7.0 release out, and I just plain didn't get to this in time. I'll definitely look at it for the next release. I don't expect that there will be such a timing lag between v0.7.0 and v0.7.1.

M.

Coordinator
Nov 24, 2011 at 3:44 PM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Nov 24, 2011 at 4:21 PM

Thanks Mark... Appreciate you entertaining this further and possibly including it in a future version... It will avoid me having to fork your code locally...

Now that my schedule is lighting it up, I will hopefully have some more time to contribute back to this great library of yours. I have a few utilities that I have developed on top of SPServices that once I clean them up a bit, I will share with others in the forums.


Enjoy the holiday.

Paul T.

Coordinator
Nov 24, 2011 at 5:44 PM
Paul:

I look forward to seeing more of your work!

M.
Dec 21, 2011 at 10:15 PM

Marc,

 

FYI... I'm sure you probably will realize this when you get around to working on incorporating this into SPSservices... but wante to make you and others aware...

I'm finding that SP has very inconsistent behavior when dealing with very large lists... Specially when Lookup Fields are indexed... In order for the use of LookupId=TRUE to work on indexed lookup fields of large Large lists, the <value> type needs to be 'Integer' (not 'text' or 'Lookup'... Example:

<Eq>
      <FieldRef Name='Release' LookupId='True' />
      <Value Type='Integer'>20</Value>
</Eq>
    

I have made the correction to the code sample I pages above on Nov 21 at 12:16 PM... 

Paul.

 

Dec 22, 2011 at 12:09 AM

That's a good tip Paul.  If you run the "GetList" operation on the list with indexed columns, is that how the column is returned?  I'm very interested in knowing if this is consistent across different deployments. :)  Here's a snippet to find the column types:

					complete: function(xData, Status) {
						//console.log(Status);
						//console.log(xData.responseText);
						//console.log(xData.responseXML.xml);
						$(xData.responseXML).find("Fields > Field").each(function() {
							//console.log( "Type: " + $(this).attr("Type") + " StaticName: " + $(this).attr("StaticName") );
							listProperties[$(this).attr("StaticName")] = $(this).attr("Type");
						});
					}

Coordinator
Dec 22, 2011 at 1:46 PM

That makes sense, Paul, since you are requesting a number (the ID of a Lookup column), not a text or lookup value. It may just be that you've been lucky with your queries in the past in that the Web Service calls haven't barfed on you.

M.

Dec 22, 2011 at 5:36 PM

Matt,

I did the "GetList" and the type associated with the column (which is index) still shows "Lookup".

Thanks.

Paul.

Coordinator
Dec 22, 2011 at 6:26 PM

When you say "LookupId='True'", you're asking for the index on the Lookup column rather than the value. The column is still a Lookup.

M.

Coordinator
Jan 13, 2012 at 6:17 PM

FYI, Paul, I'm working on this now. All of the refactoring I'm doing will make it a bit different than what you have here, but the effect will be the same.

It's also an opportunity to create some functions for redundant code, like a function getDropdownSelected(columnObj, matchOnId).

M.

Jan 13, 2012 at 8:31 PM

Marc...

Disregard by question on the work item... I just saw this post, which answer that...

Thank you... 

Paul

Coordinator
Jan 17, 2012 at 9:28 PM

Guys:

I've just posted a new alpha with this functionality. I'd appreciate any testing you might be able to do.

M.

Coordinator
Jan 17, 2012 at 9:32 PM

p.s. The option is matchOnId, which is false by default.


Jan 17, 2012 at 10:42 PM
Thanks Marc. I'll try to drop it into my app's dev environment to see if the behavior is similar to my local fork.

_____
Paul

Sent from mobile device.