Search code examples
javascriptjquerysharepoint-2010web-parts

Why am I getting, "Uncaught TypeError: getEnumerator is not a function"?


In my Sharepoint 2010 Web Part, I've got this Javascript:

function getListItemID(username, payeename, oList) {
    var arrayListEnum = oList.getEnumerator();

...which is called by this:

function upsertPostTravelListItemTravelerInfo1() {
var clientContext = SP.ClientContext.get_current();
var oList =   
  clientContext.get_web().get_lists().getByTitle('PostTravelFormFields');

this.website = clientContext.get_web();
currentUser = website.get_currentUser();

var itemCreateInfo = new SP.ListItemCreationInformation();
this.oListItem = oList.addItem(itemCreateInfo);

var travelersEmail = $('traveleremail').val();

/* If this is an update, the call to getListItemID() will return a val; otherwise (an insert), get from newly instantiated ListItem.  */
listId = getListItemID(currentUser, travelersEmail, oList);

I got the basis for this code from here.

But got the err listed above ("Uncaught TypeError: oList.getEnumerator is not a function");

One answer said I needed to add this:

<script type="text/javascript" src="/_layouts/15/sp.js" ></script>

...which I changed from "15" to "14" as that is the folder/version we're using.

That not only didn't work, but was unrecognized. I then found a clue here, namely to add this:

$(document).ready(function () { ExecuteOrDelayUntilScriptLoaded(CustomAction, "sp.js"); });

...but that only an error prior to the one already shown, namely, "Uncaught ReferenceError: CustomAction is not defined"

So what's the scoop? What is required to getEnumerator(), or otherwise retreive the ID val I need?

Here is the full code to that method, to show what I'm trying to accomplish, and how:

function getListItemID(username, payeename, oList) {
  var arrayListEnum = oList.getEnumerator();

  while (arrayListEnum.moveNext()) {
     var listItem = arrayListEnum.get_current();

     if (listItem.get_item("ptli_formPreparedBy") === username &&
         listItem.get_item("ptli_TravelersEmail") === payeename &&
         listItem.get_item("ptli_formCompleted") == false) {
       return listItem.get_id();    
     }
   }
   return '';
}

UPDATE

When I tried this (first and third lines are new):

<SharePoint:ScriptLinkID="ScriptLink1" Name="SP.js" runat="server" OnDemand="false" LoadAfterUI="true" Localizable="false"></SharePoint:ScriptLink>
<script type="text/javascript">
    SP.SOD.executeFunc('sp.js', 'SP.ClientContext', sharePointReady);

...which was inspired by a cat here, I got, "System.Web.HttpParseException was unhandled by user code Message=The server tag is not well formed."

Personally, I don't think Sharepoint is very well formed. But that's (right) beside the point (no pun intended).


Solution

  • Problem 1: You're calling getEnumerator on a list instead of a list item collection

    getEnumerator() can only be called on a list item collection (not on a List object), and only after it's been populated with items by running clientContext.executeQueryAsync()

    Problem 2: You need to call executeQueryAsync to populate the list item collection

    When using the SharePoint JavaScript client object model, your code needs to be broken up into two parts: the first part specifies what you want to get, and involves you loading queries and commands into an SPClientContext object; the second part lets you manipulate the results of the query to SharePoint, and runs as an asynchronous callback of the query execution.

    1. Create your context, specify which lists you want to access, etc.
    2. Run clientContext.executeQueryAsync() (where clientContext is an SP.ClientContext object), and pass in delegate functions to run on success or failure
    3. In your "onSuccess" delegate function, you can work with the results of the commands you loaded up in step 1

    Problem 3: You won't be able to return values directly from an asynchronously executing function

    Because step 3 above runs asynchronously, you can't get a return value from it. Any logic that depends on the results that you get in step 3 needs to be moved forward in the execution chain, using function delegation and callbacks.

    Problem 4: Inefficient filtering of list items

    This is really more of a design flaw than a show-stopping problem, but instead of having your code return every item in the list, and then using JavaScript to enumerate through the results to see if the item you want is in there, you should tell SharePoint what filter options you want before it even executes the query. Then it'll only give you items that match your query.

    Use a CAML query for this; CAML (Collaborative Application Markup Language) is an XML-based query language that SharePoint uses extensively. There are plenty of resources and tools for composing CAML queries, and you can even steal the CAML query from a SharePoint list view web part if you've already created a view that matches your query.

    Example of how to query a SharePoint list using JavaScript CSOM

    Here's an example using parts of your code:

    /* 
       ExecuteOrDelayUntilScriptLoaded(yourcode,"sp.js") makes sure 
       your code doesn't run until SP.js (the SharePoint JavaScript CSOM) 
       has been loaded
    */
    ExecuteOrDelayUntilScriptLoaded(function(){
        var payeename = $('traveleremail').val();
        var clientContext = SP.ClientContext.get_current();
        var oList = clientContext.get_web().get_lists().getByTitle('PostTravelFormFields');
    
        /* Use a CAML query to filter your results */
        var camlQuery = new SP.CamlQuery();
        camlQuery.set_viewXml('<View><Query><Where><Eq><FieldRef Name=\'ptli_TravelersEmail\' /><Value Type=\'Text\'>'+payeename+'</Value></Eq></Where></Query></View>');
    
        /* get the list item collection from the list */
        var oListItems = oList.getItems(camlQuery);
    
        /* tell SharePoint to load the list items */
        clientContext.load(oListItems);
    
        /* execute the query to get the loaded items */
        clientContext.executeQueryAsync(
            /* onSuccess Function */ 
            Function.createDelegate(this,function(){
                /* 
                   now that the query has run, you can get an enumerator 
                   from your list item collection 
                */
                var arrayListEnum = oListItems.getEnumerator();
                var ids = [];
                while(arrayListEnum.moveNext()){
                    var listItem = arrayListItem.get_current();
                    ids.push(listItem.get_id());
                }
                alert(ids.length > 0 ? "IDs of matching items: " + ids : "No matching items found!");
            }),
            /*onFailure Function*/ 
            Function.createDelegate(this,function(sender,args){
                alert("Whoops: " + args.get_message() + " " + args.get_stackTrace());
            })
        );
    },"sp.js");
    

    The CAML query in the example code only filters on the ptli_TravelersEmail column; you'd need to add some <And> elements to capture the other two filter conditions you want.