I am working with the Sharepoint Client Object Model using javascript and looking to make as much of my code as reusable as possible.
Examples of using the COM usually looks like this with a call and then a success delegate function to output the results.
function loadProfile()
{
var context = SP.ClientContext.get_current();
var web = context.get_web();
var userInfoList = web.get_siteUserInfoList();
camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('');
this.listItems = userInfoList.getItems(camlQuery);
context.load(listItems);
context.executeQueryAsync(Function.createDelegate(this, this.onProfileSuccessMethod), Function.createDelegate(this, this.onFail));
}
function onProfileSuccessMethod(sender, args)
{
var item = listItems.itemAt(0);
var first_name = item.get_item('FirstName');
var last_name = item.get_item('LastName');
alert(first_name + " " + last_name);
}
function onFail(sender, args)
{
alert('Error: ' + args.get_message() + '\n' + args.get_stackTrace());
}
What I am trying to achieve is being able return the values after instantiating an object but given that it is asynchronous I am not sure how this is possible, would I need to "listen" for the call to complete before values are returned.
Would it be something like this? Or can you not have a delegate function internal to the function it creates it from. Apologies for my limited understanding!
var profile = new loadProfile("testid");
alert(profile.onProfileSuccessMethod());
function loadProfile (id)
{
this.user_id = id;
var context = SP.ClientContext.get_current();
var web = context.get_web();
var userInfoList = web.get_siteUserInfoList();
camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('');
this.listItems = userInfoList.getItems(camlQuery);
context.load(listItems);
context.executeQueryAsync(Function.createDelegate(this, this.onProfileSuccessMethod), Function.createDelegate(this, this.onFail));
this.onProfileSuccessMethod = function()
{
var item = listItems.itemAt(0);
this.first_name = item.get_item('FirstName');
this.last_name = item.get_item('LastName');
return this.first_name
};
}
If it is asynchronous, it is impossible to return values from it. You need to use a callback pattern.
function secondStep(data){
console.log(data)
}
var profile = new loadProfile("testid", secondStep);
and
function loadProfile (id, callback)
...
this.onProfileSuccessMethod = function(){
callback({});
}
Related
I retrieve values from a SharePoint list and put them in an array itemprices.
Using console.log("Price of list item at is:" + itemprices[oListItem.id]); will print out the values of the array, but using console.log("itemprices 5: " + itemprices[5]); tells me that they are undefined:
This is the code I used:
var itemprices = [];
// Gets values from Catalogue list; but they can't be used in the Position list because of different formats
function retrieveListItems() {
var clientContext = new SP.ClientContext.get_current();
catalogueList = clientContext.get_web().get_lists().getByTitle('Catalog');
var camlQuery = new SP.CamlQuery(); // initiate the query object
camlQuery.set_viewXml('<View><Query><Where><In><FieldRef Name=\'ID\'/><Values><Value Type=\'Number\'>5</Value><Value Type=\'Number\'>6</Value><Value Type=\'Number\'>7</Value></Values></In></Where></Query></View>');
itemColl = catalogueList.getItems(camlQuery);
// returns the item collection based on the query
context.load(itemColl);
context.executeQueryAsync(retrieveListItemsSuccess, retrieveListItemsFail);
}
function retrieveListItemsSuccess() {
var listItemEnumerator = itemColl.getEnumerator();
while (listItemEnumerator.moveNext()) {
var oListItem = listItemEnumerator.get_current();
itemprices[oListItem.id] = oListItem.get_item('Preis');
console.log("itemprices 5: " + itemprices[5]);
console.log("itemprices 6: " + itemprices[6]);
console.log("itemprices 7: " + itemprices[7]);
console.log("Price of list item at is:" + itemprices[oListItem.id]);
}
}
// This function is executed if the above call fails
function retrieveListItemsFail(sender, args) {
alert('Failed to get list items. Error:' + args.get_message());
}
I don't know if this is a JavaScript issue or a SharePoint issue. What am I doing wrong?
Firstly, please use the right ClientContext object, in the code snippet above, it should be clientContext rather than context and if you want to populate field values into an array, try to use array.push, here is the modified code snippet for your reference:
<script type="text/javascript">
ExecuteOrDelayUntilScriptLoaded(retrieveListItems, 'sp.js');
var itemprices = [];
// Gets values from Catalogue list; but they can't be used in the Position list because of different formats
function retrieveListItems() {
var clientContext = new SP.ClientContext.get_current();
catalogueList = clientContext.get_web().get_lists().getByTitle('Companies');
var camlQuery = new SP.CamlQuery(); // initiate the query object
camlQuery.set_viewXml('<View><Query><Where><In><FieldRef Name=\'ID\'/><Values><Value Type=\'Number\'>5</Value><Value Type=\'Number\'>6</Value><Value Type=\'Number\'>7</Value></Values></In></Where></Query></View>');
itemColl = catalogueList.getItems(camlQuery);
// returns the item collection based on the query
clientContext.load(itemColl);
clientContext.executeQueryAsync(retrieveListItemsSuccess, retrieveListItemsFail);
}
function retrieveListItemsSuccess() {
var listItemEnumerator = itemColl.getEnumerator();
while (listItemEnumerator.moveNext()) {
var oListItem = listItemEnumerator.get_current();
itemprices.push(oListItem.get_item('Title'));
}
console.log(itemprices);
}
// This function is executed if the above call fails
function retrieveListItemsFail(sender, args) {
alert('Failed to get list items. Error:' + args.get_message());
}
</script>
This is my list data:
This is the array from Console:
You can just use this function:
itemprices.map(itemPrice => console.log(itemPrice))
The following code works to get the a list GUID by Title. What I cannot figure out is how to get the code to return that value so it can be assigned to a variable.
I am fairly new to JavaScript so the nuances trouble me. I understand most of what is going on in the code. I added the alert() in the success function however, I cannot figure out where to put the return statement to get the GUID value back.
var list;
function getListId(listTitle) {
var context = new SP.ClientContext.get_current();
var web = context.get_web();
list = web.get_lists().getByTitle(listTitle);
context.load(list, 'Id');
context.executeQueryAsync(Function.createDelegate(this,success), Function.createDelegate(this,error));
}
function success() {
var listId = list.get_id();
// console.log(listId);
alert(listId);
return listId;
}
function error(sender, args) {
alert('Request failed. ' + args.get_message() +
'\n' + args.get_stackTrace());
}
This is asynchronous, so you can't "return" it in the sense you seem to be talking about. What you would normally do, however, is execute the code you need to execute for handling the listId from within your success function:
var list;
function getListId(listTitle) {
var context = new SP.ClientContext.get_current();
var web = context.get_web();
list = web.get_lists().getByTitle(listTitle);
context.load(list, 'Id');
context.executeQueryAsync(Function.createDelegate(this,success), Function.createDelegate(this,error));
}
function success() {
var listId = list.get_id();
// console.log(listId);
alert(listId);
handleListId(listId);
}
function error(sender, args) {
alert('Request failed. ' + args.get_message() +
'\n' + args.get_stackTrace());
}
function handleListId(listId) {
//DO STUFF WITH YOUR listId HERE...
}
Sometimes there is a requirement to fetch the SharePoint taxonomy term sets programmatically. So, how to get SharePoint taxonomy terms programmatically using JSOM ?
Here is a way to fetch SharePoint taxonomy terms using JavaScript code:
$(document).ready(function () {
ExecuteOrDelayUntilScriptLoaded(function () {
SP.SOD.registerSod('sp.taxonomy.js', "/_layouts/15/sp.taxonomy.js");
SP.SOD.executeFunc('sp.taxonomy.js', false, Function.createDelegate(this, function () {
var context = SP.ClientContext.get_current();
var taxonomySession = SP.Taxonomy.TaxonomySession.getTaxonomySession(context);
var termStore = taxonomySession.get_termStores().getByName("Taxonomy_qeFlDdEX32yZ3Q7EpEIeMQ==");
var termSet = termStore.getTermSet("ed6d3beb-6a49-4444-bc5d-456f747e139d");
var terms = termSet.getAllTerms();
context.load(terms);
context.executeQueryAsync(Function.createDelegate(this, function (sender, args) {
var termsEnumerator = terms.getEnumerator();
var menuItems = new Array();
while (termsEnumerator.moveNext()) {
var currentTerm = termsEnumerator.get_current();
var targetGroups = document.getElementById("selectTaxonomy");
var taxoGroup = document.createElement("option");
taxoGroup.text = currentTerm.get_name();
targetGroups.add(taxoGroup);
}
}), Function.createDelegate(this, function (sender, args) {
alert('The error has occured: ' + args.get_message());
}));
}));
},"sp.js")
});
I'm creating a sharepoint hosted app which is based on javascript only. I wanted to update a multivalue taxonomy field in a list so I wrote this function which didn't work. there is a very little support for javascript csom online.
var list = context.get_web().get_lists().getByTitle('Company');
var item = list.getItemById(2);
var field = list.get_fields().getByInternalNameOrTitle("Departments");
var taxField = context.castTo(field, SP.Taxonomy.TaxonomyField);
var terms = new SP.Taxonomy.TaxonomyFieldValueCollection(context,
'Unit 1|5bf47d1f-d890-49d1-a844-85628ca508fd;#Unit 4|334ad23d-d2d8-4acb-ab09-38d2bacb97d4',
taxField);
taxField.setFieldValueByValueCollection(item, terms);
item.update();
context.load(taxField);
context.executeQueryAsync(
function() {
console.log('field updated');
});
I also used this code
var list = context.get_web().get_lists().getByTitle('Company');
var item = list.getItemById(2);
item.set_item('Departments', 'Unit 1|5bf47d1f-d890-49d1-a844-85628ca508fd;Unit 4|334ad23d-d2d8-4acb-ab09-38d2bacb97d4');
item.update();
context.executeQueryAsync(
function() {
console.log('field updated');
});
You must include a fake wssid (lookup id) prefix to the value. Below is a code I use to set a multiple value term field from jsom in the app model. This works.
function SetManagedMetaDataField() {
appweburl = decodeURIComponent(getQueryStringParameter('SPAppWebUrl'));
hostweburl = decodeURIComponent(getQueryStringParameter('SPHostUrl'));
context = new SP.ClientContext(appweburl);
factory = new SP.ProxyWebRequestExecutorFactory(appweburl);
context.set_webRequestExecutorFactory(factory);
appContextSite = new SP.AppContextSite(context, hostweburl);
var list = appContextSite.get_web().get_lists().getByTitle('Documents');
var item = list.getItemById(5);
var field = list.get_fields().getByInternalNameOrTitle("Cars");
var taxField = context.castTo(field, SP.Taxonomy.TaxonomyField);
var terms = new SP.Taxonomy.TaxonomyFieldValueCollection(context,
'-1;#ATS|9f3e8e20-593b-471d-a145-81ff8664fd96;#-1;#CTS|8b18f6df-22be-4548-92b4-8f240d8fbfe5',
taxField);
taxField.setFieldValueByValueCollection(item, terms);
item.update();
context.load(taxField);
context.executeQueryAsync(
function () {
alert('field updated');
}, function (sender,args) {
alert(args.get_message() + '\n' + args.get_stackTrace());
});
}
Update
I have now tried a new approach. This works when I call it one time but the second time it tells me that the collection hasnt been initiated for some reason. The comments in the test function of this code snippet will clarify my problem.
Javascript:
function test(){
//this first call works
countRetrieve('Very', 'Difficult');
//this second call generates error that collListItem hasnt been initiated
countRetrieve('Less', 'Interesting');
}
function countRetrieve(grade, title) {
var siteUrl = '/sites/MySite';
var clientContext = new SP.ClientContext(siteUrl);
var oList = clientContext.get_web().get_lists().getByTitle('Summary');
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><Query><Where>' +
'<And>' +
'<Eq><FieldRef Name=\'Grad\'/><Value Type=\'Text\'>' +
grade +
'</Value></Eq>' +
'<Eq><FieldRef Name=\'Title\'/><Value Type=\'Text\'>' +
title +
'</Value></Eq>' +
'</And>' +
'</Where></Query></View>');
this.collListItem = oList.getItems(camlQuery);
clientContext.load(collListItem);
clientContext.executeQueryAsync(Function.createDelegate(this, this.onRetrieveQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
}
function onRetrieveQuerySucceeded(sender, args) {
listItemEnumerator = collListItem.getEnumerator();
while (listItemEnumerator.moveNext()) {
var oListItem = listItemEnumerator.get_current();
itemId = oListItem.get_id();
itemCount = oListItem.get_item('Count');
}
updateCount();
}
function updateCount() {
var clientContext = new SP.ClientContext('/sites/MySite');
var oList = clientContext.get_web().get_lists().getByTitle('Summary');
this.oListItem = oList.getItemById(itemId);
//update the count, raise it by one
var c = itemCount + 1;
oListItem.set_item('Count', c);
oListItem.update();
clientContext.executeQueryAsync(Function.createDelegate(this, this.onUpdateSucceeded), Function.createDelegate(this, this.onQueryFailed));
}
function onUpdateSucceeded(sender, args){
alert('item count updated');
}
Im trying to retrieve the current value of the column "Count" in my list, and then rise this value by 1. However i get an error that tells me the Collection has not been initialized.
Didnt it get initialized in this.collListItem = oList.getItems(camlQuery); ?
It is possible this function is totally wrong, Im very grateful for tips on how to perform this task as Im new both to Sharepoint and Javascript.
This is my code (javascript):
function countUpdate() {
var siteUrl = '/sites/MySite';
var clientContext = new SP.ClientContext(siteUrl);
var oList = clientContext.get_web().get_lists().getByTitle('Summary');
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><Query><Where>' +
'<And>' +
'<Eq><FieldRef Name=\'Grade\'/><Value Type=\'Text\'>' +
'Really' +
'</Value></Eq>' +
'<Eq><FieldRef Name=\'Property\'/><Value Type=\'Text\'>' +
'Narrow' +
'</Value></Eq>' +
'</And>' +
'</Where></Query></View>');
this.collListItem = oList.getItems(camlQuery);
clientContext.load(collListItem);
clientContext.executeQueryAsync(Function.createDelegate(this, this.onUpdateQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
//update
var listItemEnumerator = collListItem.getEnumerator();
while (listItemEnumerator.moveNext()) {
var oListItem = listItemEnumerator.get_current();
var count = oListItem.get_item('Count');
oListItem.set_item('Count', '40');
oListItem.update();
}
clientContext.executeQueryAsync(Function.createDelegate(this, this.onUpdateQuerySucceeded),Function.createDelegate(this, this.onQueryFailed));
}
As answered your problem lies in the asynchronous nature of AJAX. Your call is made and other code IMMEDIATELY continues BEFORE it's returned. So what you need is a "handler" - a function to call when your request is complete that will handle the data returned by it.
executeQueryAsync takes as it's first parameter a method (a function) "succeededCallback" - you need to create that method and give it to this call (with no parens - you want to pass a reference to the function, not run it). That method will be automatically called when the request completes and, again automatically, include the results of the call as the first argument.
Looking at your code and being as simplistic as possible you're going to have to chain together three functions:
1) Sets up the initial call and when done calls 2).
2) Accepts the results from the initial call, acts upon them, sets up the second call and when done calls 3)
3) Accepts the results from the second call, acts upon them.
You can use the built in the SP functions but honestly I find them a little cumbersome. I built my own AJAX abstrction to do this here:
http://depressedpress.com/javascript-extensions/dp_ajax/
The implementation is a bit different but the ideas are exactly the same - perhaps looking through some of the examples there might bring things into clearer focus.
Hope this helps. For the little that it's worth you're in good company: this is one of those "nobody ever gets it until they really get it" things that seems to be a stumbling block for pretty anybody the first time.
You need to continue execution in onUpdateQuerySucceeded function - before that list will not have any information as request need to be send to server and response hamdled before you get items.
The functions are called XXXXAsync exactly for this reason - calling it does not give result immediatelly, but rater asynchronously in success callback.
Thanks for answer Jim. Not only question owner finds your post useful.
To easien things up i'm posting a sample:
This code can update multiple SPListItems by clicking on one CheckBox
var appOpsUrl = '/sites/AppOpsRep';
var coll;
function UpdateTargetInReport(checkBox, refValueOne, refValueTwo)
{
var clientContext = new SP.ClientContext.get_current();
var list = clientContext.get_web().get_lists().getByTitle('SLA Fulfillment');
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><Query><Where><And><Eq><FieldRef Name=\'Application\' /><Value Type=\'Text\'>' + refValueOne + '</Value></Eq><Eq><FieldRef Name=\'Metric_x0020_name\' /><Value Type=\'Text\'>' + refValueTwo + '</Value></Eq></And></Where></Query></View>');
coll = list.getItems(camlQuery);
clientContext.load(coll);
clientContext.executeQueryAsync(Function.createDelegate(this, function(){ UpdateTargetInReport_onQuerySucceeded(refValueTwo, checkBox); }), Function.createDelegate(this, function(sender,args) { UpdateTargetInReport_onQueryFailed(sender, args, checkBox); }));
}
function UpdateTargetInReport_onQuerySucceeded(refValueTwo, checkBox)
{
var clientContext = new SP.ClientContext.get_current();
var listItemEnumerator = coll.getEnumerator();
while(listItemEnumerator.moveNext())
{
var listItem = listItemEnumerator.get_current();
listItem.set_item('In_x0020_Report', checkBox.checked);
listItem.update();
}
clientContext.executeQueryAsync(Function.createDelegate(this, function() { UpdateTargetInReport_onUpdateQuerySucceeded(refValueTwo); }), Function.createDelegate(this, function(sender,args) { UpdateTargetInReport_onUpdateQueryFailed(sender, args, checkBox); } ));
}
function UpdateTargetInReport_onQueryFailed(sender, args, checkBox)
{
SP.UI.Notify.addNotification('Request failed. ' + args.get_message());
checkBox.checked = !checkBox.checked;
}
function UpdateTargetInReport_onUpdateQuerySucceeded(refValueTwo)
{
SP.UI.Notify.addNotification(refValueTwo + ' updated successfully');
}
function UpdateTargetInReport_onUpdateQueryFailed(sender, args, checkBox)
{
SP.UI.Notify.addNotification('Request failed. ' + args.get_message());
checkBox.checked = !checkBox.checked;
}