Sharepoint 2010 JSOM getEnumerator 'The collection has not been initialized. It has not been requested...' - javascript

I'm no stranger to this error or the various solutions, but this one has me scratching my head. I'm using JavaScript object model to get all a list of all the files in a given folder. I get the error on the getEnumerator in the code below. I've stripped the code down to the bare minimum:
function getFilesInFolder() {
var folderServerRelativeUrl = folderPath + ID;
var context = new SP.ClientContext.get_current();
var web = context.get_web();
var list = web.get_lists().getByTitle(documentLibraryName);
var query = SP.CamlQuery.createAllItemsQuery();
query.set_folderServerRelativeUrl(folderServerRelativeUrl);
//Update "web part" link
$("#doclink").attr('href',folderServerRelativeUrl);
files = list.getItems(query)
context.load(files, 'Include(Id)');
context.executeQueryAsync(Function.createDelegate(this, this.OnSuccess), Function.createDelegate(this, this.OnFailure));
}
function OnSuccess()
{
//ERROR Next Line:
var listItemEnumerator = this.files.getEnumerator();
var table = $("#attachments");
while (listItemEnumerator.moveNext())
{
console.log("Found a file");
}
}
The code is called as such in the beginning of the file:
$(document).ready(function(){
//Other code...
ExecuteorDelayUntilScriptLoaded(getFilesInFolder,"sp.js");
});
I've tried a ton of variations on this AND it used to work (not sure what changed either server or client-side).

I tried your code, since I couldnt see any obvious errors, and it works.
I hardcoded the folderServerRelativeUrl as it works in Swedish:
"/sites/intranet/dokument" is my root web and the "Documents" folder.
You can try in the broswer "sitecollection/_api/web/getFolderByServerRelativeUrl('/path/to/folder/')"
To see if the url u are using is a correct one.
You could also set a breakpoint in your onsuccess and look in the console: files.get_count() to see if you have any results.
Your load is fine, so dont worry about that!
function getFilesInFolder() {
var folderServerRelativeUrl = "/sites/intranet/dokument";
var context = new SP.ClientContext.get_current();
var web = context.get_web();
var list = web.get_lists().getByTitle("Dokument");
var query = SP.CamlQuery.createAllItemsQuery();
query.set_folderServerRelativeUrl(folderServerRelativeUrl);
//Update "web part" link
// $("#doclink").attr('href',folderServerRelativeUrl);
files = list.getItems(query)
context.load(files, 'Include(Id)');
context.executeQueryAsync(Function.createDelegate(this, this.OnSuccess), Function.createDelegate(this, this.OnFailure));
}
function OnSuccess()
{
//ERROR Next Line:
var listItemEnumerator = this.files.getEnumerator();
var table = $("#attachments");
while (listItemEnumerator.moveNext())
{
console.log("Found a file");
}
}

This error basically means that resulting object (this.files variable) has not been requested from the server once the iteration is performed. This error would be reproduced by executing simultaneously several queries (basically invoking getFilesInFolder several times).
Given the fact that result object (this.files) is stored in global scope (window object) i would strongly suggest against this approach of getting list items, instead you could consider to store the result in local scope as demonstrated below:
function getFilesInFolder() {
var folderServerRelativeUrl = "/Documents/Archive";
var context = new SP.ClientContext.get_current();
var web = context.get_web();
var list = web.get_lists().getByTitle(documentLibraryName);
var query = SP.CamlQuery.createAllItemsQuery();
query.set_folderServerRelativeUrl(folderServerRelativeUrl);
var items = list.getItems(query)
context.load(items, 'Include(Id)');
context.executeQueryAsync(
function(){
if(items.get_count() > 0)
{
console.log('Found a file(s)');
}
},
function(sender,args){
console.log(args.get_message());
});
}
With such approach you could easily avoid conflicts with overriding resulting object and therefore getting this exception.

Related

Fetch data from multiple sheets using Google app script

Trying to fetch data from multiple spreadsheet's. All Sheet's are stored in same folder. Have to fetch data in one master sheet from only specific files by file name. I have written below script. It's working fine if we enter only one file name in specified range (in master sheet tab) (getSheetByName) but showing error while trying to fetch data for multiple files.
Showing error - "TypeError: Cannot read property 'length' of undefined"
Below is Script -
function get_compiled_data() {
var filelist = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("FileName");
var filelists = filelist.getRange("A2:A").getValues();
var folder = DriveApp.getFolderById("1li9hBP_W5gPkb_ASKqin4j1ZGEn1Gvji");
var fileindex = folder.getFilesByName(filelists);
var file;
var filetype;
var sheetID;
var collect_data = [];
var data;
while (fileindex.hasNext()) {
file = fileindex.next();
filetype = file.getMimeType();
if (filetype === "application/vnd.google-apps.spreadsheet"){
sheetID = file.getId();
data = getData(sheetID);
data = data.map(function(r){return r.concat([file.getName()]);});
collect_data = collect_data.concat(data);
}
}
var target = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Compiled_Data");
target.getRange("A2:AX").clearContent();
target.getRange(2, 1, collect_data.length, collect_data[0].length).setValues(collect_data);
}
function getData (sheetID) {
var sheet = SpreadsheetApp.openById(sheetID);
var tab = sheet.getSheets()[0];
var data = tab.getRange("A3:AX" + tab.getLastRow()).getValues();
return data;
}
Issue:
You are providing a 2D array to getFilesByName(name), when you should be providing a string. Because of this, the code never enters the while block and collect_data remains an empty array, causing collect_data[0] to be undefined, and producing the observed error when trying to access its length.
When you were looking for a single file name you were probably using getValue(), which retrieves a single value, which can be a string, which can be used in getFilesByName. getValues() returns a 2D array instead, so you should adapt your code so that it iterates through each value returned by getValues().
Solution:
Edit the getValues() line and wrap all the actions made from getFilesByName inside a loop the following way:
function get_compiled_data() {
var filelist = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("FileName");
var filelists = filelist.getRange(2, 1, filelist.getLastRow() - 1).getValues();
var folder = DriveApp.getFolderById("FOLDER-ID");
filelists.forEach(fileName => {
var fileindex = folder.getFilesByName(fileName[0]);
// Rest of code
});
});
Reference:
getValues()
getValue()

Javascript Asynchronous Issue (Sharepoint)

Hopefully somebody can show me a way to do this, and explain why this code is failing. I believe it is a scope issue but have banging my head on it for a few days.
Let me first explain what the code is doing. I have a HTML button on a web part aspx page that runs a function in a js file. The code searches my Sharepoint list for items that are missing two field values, via a CAML query. It then loops through the items, and calls an asynchronous web service for each item. The asynchronous event returns a promise, that I use the ,to update each of the items with the 2 values retuned by the asynchronous event. Each item is then saved back to the server, which is where my issue is occurring. When I debug this, the Function.createDelegate(this, this.success) has an udefined argument (b), that blows up on b.apply(). This is definitely not my strong suit in javascript. Can somebody help? See Code below:
Note - See //*Save Comment in Asychronous Function section for line which is causing issues.
HTML Button Calls Function
function UpdateSegmentSubsegment()
{
//1.) Query items where segment & sub-segment blank returnin array of items *
//2.) Loop through retuned items *
//3.) Get employee id *
//4.) Call Web Service and return segment & subsegment for employee id
//5.) Update item with segment & subsegment + Save
//6.)Loop
//Query Information Variables
var listname='ERD - XXXXX';
var caml='<View Scope="Recursive"><Query><Where><And>' +
'<IsNull><FieldRef Name="Employee_x0020_Business_x0020_Se" /></IsNull>' +
'<IsNull><FieldRef Name="Employee_x0020_Business_x0020_Su" /></IsNull>' +
'</And></Where></Query></View>';
//Function Variables
var itemcount=0;
var context = new SP.ClientContext.get_current();
QueryListItems(listname,caml,context).then(function(items){
//Get item count returned from promise
itemcount=items.get_count();
window.status="Preparing to Process"+itemcount.toString()+" items..."
//Prepare Looping object
var listitemenumerator=items.getEnumerator();
//Loop through each List Item
while (listitemenumerator.moveNext()) {
//Get Employee ID from list item collection
var listitem = listitemenumerator.get_current();
var employeeid=listitem.get_item("EmpUseAcct");
//Check for invalid item value
if(employeeid!="#Value!"){
//Ansychronous Function Call
QueryHRInformation(employeeid,listitem,context);
}
else
{
//Log CA Skip
window.status="Skiping CA Number "+listitem.get_item("Title").toString();
}
};
});
}
Query Function
//Query Promise
function QueryListItems(listname, CAMLQ,clientContext)
{
//Defererred Object
var deferred = new $.Deferred();
//Sharepoint Object Model
//Get List
var list = clientContext.get_web().get_lists().getByTitle(listname);
//CAML Query
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml(CAMLQ);
var items = list.getItems(camlQuery);
//Get Items
clientContext.load(items);
clientContext.executeQueryAsync(
Function.createDelegate(this,
function () { deferred.resolve(items); }),
Function.createDelegate(this,
function (sender, args) { deferred.reject(sender, args); }));
return deferred.promise();
}
Asychronous Function
//Asnchronous Function Call
function QueryHRInformation(employeeid,listitem,clientContext){
//Web Service Variables
var curequestorID="XXXXXX";
var wsserviceID="someweb-serviceid";
var wsservicePassword="somepassword";
var appIdentifier="someid";
var wsserviceName="SomeDetails";
//Debugging Variable to check async list item object
var title;
//Asynchronous Call
XXX.ERD.BizTalk.XX.getInstance().getInfo(employeeid,curequestorID, wsserviceID, wsservicePassword, appIdentifier, wsserviceName).then(function(empResult){
//Debugging Check
title=listitem.get_item("Title");
//Set list item values from web service object return (empResult)
listitem.set_item("Employee Business Segment",empResult.BusSegment);
listitem.set_item("Employee Business Sub Segment",empResult.SubSegment);
//Update list item values
listitem.update();
//*Save Item
clientContext.executeQueryAsync(Function.createDelegate(this, this.success), Function.createDelegate(this, this.failed));**
});
}
Well I never leave my questions unanswered so others in the development community may benefit from my questions. Too many posts go unanswered, or just end after a solution is reached.
Yes, I figured it to be scoping issue a few weeks back, and solved my own issue. I wrapped them into functions passing along the values. When it is sent to the stack, it will not lose the scope of the values from the previous asynchronous call.
Inside the first function that makes an asynchrounous I pass the values and call this function:
SaveItem(listitem, clientContext, businesssegment,subsegment);
Here is the complete code:
function UpdateSegmentSubsegment()
{
//Prompt for Batching Note: Leave it blank to do all.
var camlrowlimit=prompt("Please enter the batch query","");
var camlbatch;
//Check Batch Input
if(camlrowlimit!=null)
{
//Set Dynamic RowLimit Variable
if(camlrowlimit=="")
{
camlbatch="";
}
else
{
camlbatch="<RowLimit>"+camlrowlimit+"</RowLimit>";
}
//Query Information Variables
var listname='ERD - XXXXX';
var caml='<View Scope="Recursive"><Query><Where><And>' +
'<IsNull><FieldRef Name="Employee_x0020_Business_x0020_Se" /></IsNull>' +
'<IsNull><FieldRef Name="Employee_x0020_Business_x0020_Su" /></IsNull>' +
'</And></Where></Query>'+camlbatch+'</View>';
//Function Variables
var itemcount=0;
var context = new SP.ClientContext.get_current();
QueryListItems(listname,caml,context).then(function(items){
//Get item count returned from promise
itemcount=items.get_count();
window.status="Preparing to Process "+itemcount.toString()+" items..."
//Prepare Looping object
var listitemenumerator=items.getEnumerator();
//Loop through each List Item
while (listitemenumerator.moveNext()) {
//Get Employee ID from list item collection
var listitem = listitemenumerator.get_current();
var employeeid=listitem.get_item("EmpUseAcct");
//Check for invalid item value
if(employeeid!="#Value!"){
//Ansychronous Function Call
QueryHRInformation(employeeid,listitem,context);
}
else
{
//Log CA Skip
console.log("Skiping CA Number "+listitem.get_item("CorrectiveActionNumber"));
}
};
});
}
}
//Query Promise
function QueryListItems(listname, CAMLQ,clientContext)
{
//Defererred Object
var deferred = new $.Deferred();
//Sharepoint Object Model
//Get List
var list = clientContext.get_web().get_lists().getByTitle(listname);
//CAML Query
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml(CAMLQ);
var items = list.getItems(camlQuery);
//Get Items
clientContext.load(items);
clientContext.executeQueryAsync(
Function.createDelegate(this,
function () { deferred.resolve(items); }),
Function.createDelegate(this,
function (sender, args) { deferred.reject(sender, args); }));
return deferred.promise();
}
//Asnchronous Function Call
function QueryHRInformation(employeeid,listitem,clientContext){
//Web Service Variables
var curequestorID="XXXXXX";
var wsserviceID="XXXXXX";
var wsservicePassword="XXXXXX";
var appIdentifier="XXXXXX";
var wsserviceName="XXXXXXX";
//Debugging Variable to check async list item object
//var title;
//Asynchronous Call
XXX.ERD.BizTalk.XX.getInstance().getInfo(employeeid,curequestorID, wsserviceID, wsservicePassword, appIdentifier, wsserviceName).then(function(empResult){
//Empresult.status
var businesssegment=empResult.BusSegment;
var subsegment= empResult.SubSegment;
SaveItem(listitem, clientContext, businesssegment,subsegment);
//*Save Item
//clientContext.executeQueryAsync(Function.createDelegate(this, this.success), Function.createDelegate(this, this.failed));
})
.fail(function(xhr, status, msg){
console.log(msg);
});
}
function SaveItem(item,ctx,bsg,ssg)
{
var refno;
//Debugging Check
refno=item.get_item("CorrectiveActionNumber");
//Set Fields
item.set_item("Employee_x0020_Business_x0020_Se",bsg);
item.set_item("Employee_x0020_Business_x0020_Su",ssg);
//Update list item values
item.update();
//ctx.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded),Function.createDelegate(this, this.onQueryFailed));
ctx.executeQueryAsync(Function.createDelegate(this,function(){ onQuerySucceeded(refno);}),Function.createDelegate(this, this.onQueryFailed));
}
function onQuerySucceeded(result, args) {
console.log('Saved CA Number'+result);
}
function onQueryFailed(sender, args) {
console.log('Request failed. ' + args.get_message()+'\n' + args.get_stackTrace());
}
This should really be a comment but since do not have enough 'reputation'...
Anyway, to check whether it is a scoping issue, replace the issue line with this:
clientContext.executeQueryAsync(function(){console.log("Success"},function(){console.log("failed"})
If this resolves the issue then it was some sort of scoping issue.
Also, see this link for the correct syntax for updating item (i.e try to declare client context etc again when updating):
https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/complete-basic-operations-using-javascript-library-code-in-sharepoint#create-update-and-delete-list-items
Finally, I recommend using this library:
https://aymkdn.github.io/SharepointPlus/

Successful API call, but can't access results throughout the script

I'm having trouble accessing API results in syllables from outside of the getSyll.onload = function(){} scope. The data is displayed fine through console.log(syllables) where it's located above, but accessing the same data elsewhere in the program isn't possible.
I've tried window.syllables to no avail.
I'm aware the problem has to due with local/global variable scope, but I can't seem to figure it out, even after going through other posts regarding the topic.
var syllables = [];
function getSyllables(targetWord) {
var getSyll = new XMLHttpRequest();
getSyll.open("GET", "https://wordsapiv1.p.mashape.com/words/" + targetWord + "/syllables");
getSyll.setRequestHeader("X-Mashape-Key", "key")
getSyll.onload = function() {
var data = JSON.parse(getSyll.responseText);
syllables = data.syllables.list;
console.log(syllables);
};
getSyll.send();
}

Using Google App Script to get values from sheets and display them in text box

So, Google recently updated Google App Script API and added lots of nice features, however, in the process, they also depreciated LOTS of API. I have been working on a Library Database user interface for the place I work on my college campus, and when I wanted to update my app to the new API, a lot of things broke, and I can't figure out how to make them work again.
What I am trying to do is get a value from a Google Sheets file, and simply put that value in a text box on the web app. Currently I cannot get that work work. In addition, I discovered something that was troublesome, and that is, the debugger seems to not be correct. I know, bold accusation. Let me try to show you.
Code.gs
function doGet(e) {
var html = HtmlService.createHtmlOutputFromFile('index')
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
return html;
}
function searchBooks(searchItem, searchType){
var sI = searchItem;
Logger.log(sI);
var sT = searchType;
Logger.log(sT);
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var ss = sheets[0];
var itemDataRange = ss.getRangeByName("itemInformation");
var selectedItem = null; //the item that will be returned
//var selectedSearch = searchItem;
var titles = sheet.getRange("K2:K9507").getValues(); //get the titles of the items
var authors = sheet.getRange("J2:J9507").getValues(); //get the authors in the sheet
var barcodes = sheet.getRange("B2:B9507").getValues(); //get the barcodes in the sheet
var itemsArray = new Array();
if (sT == '')
{
return null;
}
else if (sT.value == 'Please select type...')
{
var test = "this works";
Logger.log(test);
return selectedItem;
}
else if(sT == 'Barcode')
{
var selectedBarcode = sI;
for(var i = 0; i < barcodes.length; i++) //search for the barcode
{
if(barcodes[i] == selectedBarcode)
{
selectedItem = titles[i];
break; //break immediately because barcodes are not duplicated
}
}
if(selectedItem != null)
{
return selectedItem;
}
else
{
selectedItem = "No book(s) found";
return selectedItem;
}
return selectedItem;
}
}
...
index.html
<script>
function bookSearch()
{
var searchItem = String(document.getElementById('searchItem').value.toLowerCase());
var searchType = String(document.getElementById('searchType').value.toLowerCase());
google.script.run.withSuccessHandler(bookFound).searchBooks(searchItem, searchType);
}
...
function bookFound(selectedItem)
{
document.getElementById("bookResultBox").innHTML = selectedItem;
alert(selectedItem);
}
</script>
When I test this code, and put a search value with the category "Barcodes" selected, I successfully get console logs of the data being brought into the function searchBooks, however the debug console says that the variables sI, sT, searchItems, and searchType are all undefined.
I've also been having trouble trying to figure out the proper API calls to use to search through the spreadsheet (when dealing with stuff like getRangeByName). I think there might be a slightly different way to do this since the big update. I may have had it working before I changed some of the code, although I started changing a lot of it when I was trying to figure out WHY nothing was displaying. When I saw at the "undefined" debug console logs, it scared me a bit. I can't tell if I'm messing up, or the API is messing up.
Any help is much appreciated in advance :)
There's probably an error in your code. It's probably coming from line:
var itemDataRange = ss.getRangeByName("itemInformation");
Your variable ss is not a spreadsheet class, it's a sheet class. You can't get a RangeByName of a sheet class. There is no getRangeByName() method of the Sheet class.
I'd change your code to this:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var itemDataRange = ss.getRangeByName("itemInformation");
If you need to get the first sheet:
var theFirstSheet = ss.getSheets()[0];

Copy selected List-Items to an other list with ECMA Script

i need a script that copies all my selected list items to an other (custom) list. I found nice solution for documents:
var context = SP.ClientContext.get_current();
var web = context.get_web();
context.load(web);
var _destinationlib = web.get_lists().getByTitle('DestinationLibrary');
context.load(_destinationlib);
var notifyId;
var currentlibid = SP.ListOperation.Selection.getSelectedList();
var currentLib = web.get_lists().getById(currentlibid);
var selectedItems = SP.ListOperation.Selection.getSelectedItems(context);
var count = CountDictionary(selectedItems);
for(var i in selectedItems)
{
alert('Now copying ' + i);
var currentItem = currentLib.getItemById(selectedItems[i].id);
context.load(currentItem);
var File = currentItem.get_file();
context.load(File);
//Excecuting executeQueryAsync to get the loaded values
context.executeQueryAsync
(
function (sender, args) {
if(File != null) {
var _destinationlibUrl = web.get_serverRelativeUrl() + _destinationlib.get_title() + '/' + File.get_name();
File.copyTo(_destinationlibUrl, true);
notifyId = SP.UI.Notify.addNotification('Moving fileā€¦' + File.get_serverRelativeUrl() + 'to' + _destinationlibUrl, true);
//Excecuting executeQueryAsync to copy the file
context.executeQueryAsync(
function (sender, args) {
SP.UI.Notify.removeNotification(notifyId);
SP.UI.Notify.addNotification('File copied successfully', false);
},
function (sender, args) {
SP.UI.Notify.addNotification('Error copying file', false);
SP.UI.Notify.removeNotification(notifyId);
showError(args.get_message());
});
}
},
function (sender, args) {
alert('Error occured' + args.get_message());
}
);
}
I dont know what i have to change to get it working for normal list items. I tried to exchange
var File = currentItem.get_file();
context.load(File);
with
var title = currentItem.get_Title();
context.load(title);
var number = currentItem.get_item('number');
context.load(number);
but it dosnt work. It would be great if somebody can give me a hint what i have to do.
many thx
Fabulus
It looks like that you took code above from here.
Try to be attentively. This code copies selected files (not list items!) to another document library!
For your needs better try to code your own solution. See SharePoint JavaScript Class Library for details. You can have two possible architectures:
Make all work from JavaScript. And the first your step will be addItem method of SP.List.
Make processing of selection on client in JavaScript and call your custom server-side component (may be an application page) for items copying (creating copies in new list of already existed items from initial list.). See this for example.
Also be careful with context.load. It's recommended to write all next code in context.executeQueryAsync. Use Firebug in FF and developer tools in Chrome for debugging your code and to find what is wrong.

Categories