Optimal/preferred way to call 'SP.ClientContext.executeQueryAsync' in SharePoint - javascript

I have been learning client-side object model and came across the method executeQueryAsync. I found there are quite a few ways to call this method. Some of the one I found were these:
var context = new SP.ClientContext.get_current();
// Option 1
context.executeQueryAsync(
function(sender, args){ },
function(sender, args){ }
);
// Option 2
context.executeQueryAsync(
Function.createDelegate(this, _onSucceed),
Function.createDelegate(this, _onFail)
);
// Option 3
context.executeQueryAsync(
Function.createDelegate(this, this._onSucceed),
Function.createDelegate(this, this._onFail)
);
// Option 4
context.executeQueryAsync(_onSucceed, _onFail);
Which of this way is the most optimal/preferred one? Also what does the statement Function.createDelegate do? The documentation for this function seems to be very cryptic for me.

First I would say there is no 'optimal way' as these all just behave somewhat differently... Second, I would add this isn't so much a SharePoint or executeQueryAsync specific thing as it is a JS thing in general...
Next we need to understand that executeQueryAsync expects two functions as arguments: the first is a function to perform if executeQueryAsync succeeds, the second is a function to perform if the method encounters an error. These functions are passed parameters (from executeQueryAsync, not from your JS) representing the sending object as well as an arguments object that can have some data (args.get_message() and args.get_stackTrace() are common in the case of a failed call)
In your 'Option 1' example, executeQueryAsync is given two anonymous functions, you won't be able to re-use them anywhere, but if the behavior is simple this may be sufficient.
In Option 2 you use the createDelegate method to give the success and failure callbacks a context -- this speaks to scoping within JavaScript; if you need to reference a variable that is only accessible in the function that calls executeQueryAsync, you'll need to use this sort of pattern so that this within the callback references the function that called executeQueryAsync instead of the success or failure function that you're now in. You can think of creating a delegate as the calling function calling on some other function, but saying 'I want that function to be able to see what I can see no matter where it's located at within the code.' This may all seem a bit arcane, but such is scoping within JavaScript... You could completely circumvent the need for doing this by referencing variables at higher scope levels (say inside of a function that contains the calling method as well as the success and failure methods)
Option 3 is just like Option 2, except it just specifies that the _onSucceed or _onFail functions should be the ones that are contained within the calling object
Option4 is just like Option 1, except that you've named the functions (and that they are available within the current scope) and are calling them by name.
I usually use something like option 2, or option 4 -- but I hope you can see that it really just depends on how you're trying to structure your code.
EDIT:
In response to a comment about Function.createDelagate() -- It seems to just be a helper in an ASP.NET script resource; it does nothing other than calling apply() (which is the standard JS way of doing this -- see MDN documentation here). It might also provide some backward compatibility somewhere within ASP.NET, but I'm not really sure!
Here is the code for the function from a script resource file in my SP environment:
Function.createDelegate = function(a, b) {
return function() {
return b.apply(a, arguments)
}
};
And as a bonus, I was thinking about how I use executeQueryAsync and I realized that I actually use it more often like option 1, with a promise pattern using jQuery deferreds like this:
function getSPDataAsync(context) {
var deferred = $.Deferred();
context.executeQueryAsync(function(sender, args) {
deferred.resolve(sender, args);
}, function(sender, args) {
deferred.reject(sender, args);
});
return deferred.promise();
}
Then you can do things a little less-spaghetti-like, such as:
...
ctx.load(items);
getSPDataAsync(ctx).then(function() {
//do some stuff with the data when the promise resolves
});
Just in case anyone cares! :)

Please try the below answer...this should help..Below code uses the context.ExecutequeryAsync method but since the items are captured separately on a string array there should not be any issues with respect to asynchronous behavior..
<style>
table { table-layout: fixed; }
td { width: 50%; }
</style><script type="text/javascript">
function ShowselectedItems() {
var ctx = new SP.ClientContext.get_current();
web = ctx.get_web();
if (ctx != undefined && ctx != null) {
var listId = SP.ListOperation.Selection.getSelectedList();
var oList = ctx.get_web().get_lists().getByTitle('Testform'); // Put your list name here
var selectedItems = SP.ListOperation.Selection.getSelectedItems(ctx);
var camlquerystr = '';
if(selectedItems.length > 0){
if(selectedItems.length == 1)
{
camlquerystr += '<Where><Eq><FieldRef Name=\'ID\'/><Value Type=\'Number\'>' + selectedItems
[0].id + '</Value></Eq></Where>';
}
else if(selectedItems.length == 2)
{
camlquerystr += '<Where><Or><Eq><FieldRef Name=\'ID\'/><Value Type=\'Number\'>' + selectedItems
[0].id + '</Value></Eq><Eq><FieldRef Name=\'ID\'/><Value Type=\'Number\'>' + selectedItems[1].id +
'</Value></Eq></Or></Where>';
}
else
{
var i;
camlquerystr += '<Where>';
for (i = 0; i < selectedItems.length - 1; i++) {
camlquerystr += '<Or><Eq><FieldRef Name=\'ID\'/><Value Type=\'Number\'>' + selectedItems
[i].id + '</Value></Eq>';
}
camlquerystr += '<Eq><FieldRef Name=\'ID\'/><Value Type=\'Number\'>' + selectedItems[i].id +
'</Value></Eq>';
for (i = 0; i < selectedItems.length - 1; i++) {
camlquerystr += '</Or>';
}
camlquerystr += '</Where>';
}
}
else
{
alert('Please select your item');
retrun;
}
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><Query>' + camlquerystr + '</Query></View>');
this.collListItem = oList.getItems(camlQuery);
ctx.load(collListItem, 'Include(Id, Title,Name,First_x0020_Name,Last_x0020_Name)');
ctx.executeQueryAsync(Function.createDelegate(this, this.success), Function.createDelegate(this,
this.failed));
}
}
function success() {
var listItemInfo = '';
var headstr = "<html><head><title></title></head><body>";
var footstr = "</body>";
var content;
var listItemEnumerator = collListItem.getEnumerator();
while (listItemEnumerator.moveNext()) {
var oListItem = listItemEnumerator.get_current();
content += "<table border='1' width='100%' style='table-layout: fixed;'><tr><td>Title:</td><td>" + oListItem.get_item('Title') + "</td></tr><tr><td>Name:</td><td>" + oListItem.get_item('Name') + "</td></tr><tr><td>First Name:</td><td>" + oListItem.get_item('First_x0020_Name') + "</td></tr><tr><td>LastName:</td><td>" + oListItem.get_item('Last_x0020_Name') + "</td></tr></table><br/><br/>";
}
w = window.open("", "_blank", "k");
w.document.write(headstr + content + footstr);
w.print();
}
function failed(sender, args) {
alert('failed. Message:' + args.get_message());
}
</script>Show Items​​​​​

Related

Return variable in .executeQueryAsync

I have this code which alert the informations i want but i don't know how to modify this to display my select list, i just need to know how can i change this code and replace alert(listItemInfo); by return listItemInfo; to display my custom list instead of the default list of SharePoint
var lookupSample = lookupSample || {};
var siteUrl = '/sites/studentday/bat/testJS';
lookupSample.CustomizeFieldRendering = function() {
// Intialize the variables for overrides objects
var overrideCtx = {
Templates: {
Fields: {
'Supplier': {
'NewForm': ExecuteOrDelayUntilScriptLoaded(lookupSample.singleLookupValue, "sp.js")
}
}
}
};
// Register the override of the field
SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);
}
lookupSample.singleLookupValue = function(ctx) {
var clientContext = new SP.ClientContext(siteUrl);
var oList = clientContext.get_web().get_lists().getByTitle('Suppliers');
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><Query><GroupBy><FieldRef Name = "Category"/></GroupBy><OrderBy><FieldRef Name = "Title"/></OrderBy></Query></View>');
this.collListItem = oList.getItems(camlQuery);
clientContext.load(collListItem);
clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
}
function onQuerySucceeded(sender, args) {
var listItemInfo = [];
var listItemEnumerator = collListItem.getEnumerator();
var tempo = '';
listItemInfo.push('<select id="DdList" style="width: 200px;">');
while (listItemEnumerator.moveNext()) {
var oListItem = listItemEnumerator.get_current();
if (tempo == oListItem.get_item('Category')) {
listItemInfo.push('\n <option value ="' + oListItem.get_id() + '">' + oListItem.get_item('Title') + '</option>');
} else {
if (tempo != "") {
listItemInfo.push('\n </optgroup>');
}
listItemInfo.push('\n <optgroup label ="' + oListItem.get_item('Category') + '">');
listItemInfo.push('\n <option value ="' + oListItem.get_id() + '">' + oListItem.get_item('Title') + '</option>');
tempo = oListItem.get_item('Category');
}
}
listItemInfo.push('\n </optgroup>');
listItemInfo.push('\n</select>');
alert(listItemInfo);
}
function onQueryFailed(sender, args) {
listItemInfo.push('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
lookupSample.CustomizeFieldRendering();
You cannot directly return a value from an asynchronous function.
Instead of returning a value at the end of the async function, write a new function to do whatever it is you were going to do with that return value, and call that function.
For example, instead of this:
var asyncResult = asyncDoSomething();
doSomethingWithResult(asyncResult); // WRONG
function asyncDoSomething(){
var something;
// logic here
return something;
}
...follow this pattern:
asyncDoSomething();
// no more code should be executed in this context after calling async function
function asyncDoSomething(){
var something;
// logic here
doSomethingWithResult(something); // RIGHT: logic moved forward
}
Don't put additional lines of code within the same scope subsequent to invoking the asynchronous function.
Instead, pass that logic forward, which may mean embedding it into the asynchronous function or (more often) passing a callback function to the asynchronous function for it to invoke when it's done with whatever it does.
Modern libraries often work around this pattern by having asynchronous functions accept two parameters that indicate which functions to invoke upon completion. executeQueryAsync for example has two parameters: a function to execute if it succeeds and a function to execute on failure.
JavaScript "promises" work similarly, with each promise object allowing you to specify functions to invoke when the promise is returned (defined through the then() function).

How to get Javascript object from HTML element

I have 3 "dice" objects created from this custom constructor:
function Dice() {
this.value = 0;
this.keep = false;
this.roll = function() {
this.value = Math.floor(Math.random()*6)+1;
};
}
Then, inside function rollOnce(), I have 3 HTML buttons inside a document.getElementById("paragraph1").innerHTML command that will display each dice's value as follows:
function rollOnce() {
(...)
document.getElementById("paragraph1").innerHTML =
'<button id="diceOne" class="unkept" onclick="keepDice(this.id)">'+dice1.value+'</button> ' +
'<button id="diceTwo" class="unkept" onclick="keepDice(this.id)">'+dice2.value+'</button> ' +
'<button id="diceThree" class="unkept" onclick="keepDice(this.id)">'+dice3.value+'</button> ';
}
Now, function keepDice(diceId) will set attribute class="kept" for each dice/button that has been clicked.
The next thing I want to do is to know which dice variable (dice1, dice2, dice3) has been clicked (in order to keep their value by doing diceN.keep = true;. Because after that there will be another round of the game in which only those dice which are "unkept" will get another diceN.roll() call. But my knowledge is still very limited and I only know how to access (HTML only) elements by using document.getElementsBy(...) (this is the HTML DOM, right? I'm currently learning this at W3Schools).
I have not yet learned about jQuery, AngularJS and all the other cool webdev stuff. So if it is possible to answer using only Javascript it would be much appreciated (even if other libs would make it easier! It's a bonus if there are alternative solutions and I would be happy to learn too!). Is this possible at all?
Thanks in advance,
Maybe something like class="kept-'+dice1.keet+'" onclick="keepDice(1)"
then
function keepDice(index){
dices[index].keep = true;
turns--;
if (turns > 0) {
rollOnce()
}
}
Try this:
function keepDice(id) {
var whichDice;
switch(id) {
case 'diceOne':
whichDice = dice1;
break;
case 'diceTwo':
whichDice = dice2;
break;
case 'diceThree':
whichDice = dice3;
break;
}
whichDice.keep = true;
}
If you stored your dice in an associative array like this:
dice['diceOne'] = new Dice();
dice['diceTwo'] = new Dice();
dice['diceThree'] = new Dice();
you would create the buttons almost the same way
<button id="diceOne" class="unkept" onclick="keepDice(this.id)">dice["diceOne"].value</button>
you could then write your dice function like this
function keepDice(id)
{
dice[id].keep = true;
document.GetElementById(id).setAttribute("class","kept");
//...
}
I came back to this again and realised there's a better way. It's quite a different approach than what you've got so far, but let me explain...
I know your question title is "How to get Javascript object from HTML element" but my answer better serves the question "How to get HTML element from Javascript object" and also better solves the problem you're facing.
First, I set the stage by creating a container element #paragraph1 and a "Roll Once" button which runs the rollOnce() function
<p id="paragraph1"></p>
<button onclick="rollOnce()">Roll Once</button>
Then I create the Dice() Object which takes a parameter - this parameter is the id of the element we wish to use as a container. We must wait for the HTML to load before we can find that container because until then, it simply doesn't exist yet. That's why I have bound a function to the document.onreadystatechange event.
So when the HTML has loaded and the document is ready, I initialise the Object, storing it in a var and the Object has all the required functions built-in for managing it's button.
function Dice(container) {
this.button = document.createElement("button");
this.button.innerHTML = 0;
document.getElementById(container).appendChild(this.button);
this.button.addEventListener('click', function() {
this.className = 'kept';
});
this.roll = function() {
if(this.button.className != 'kept') {
this.button.innerHTML = Math.floor(Math.random()*6)+1;
}
}
}
var dice1;
var dice2;
var dice3;
document.onreadystatechange = function () {
if(document.readyState == "complete") {
dice1 = new Dice("paragraph1");
dice2 = new Dice("paragraph1");
dice3 = new Dice("paragraph1");
rollOnce();
}
}
function rollOnce() {
dice1.roll();
dice2.roll();
dice3.roll();
}
Fully working demonstration is here: http://codepen.io/anon/pen/groEmg
Edit: If you want to get the values of the dice later, you can access the Objects' properties like so: dice1.button.innerHTML
You need to keep track of what has been kept and what has not been kept. It would be useful to hold all the dice functionality inside the dice class. every time you run rollOnce() you must also represent the kept/unkept state in the className.
Here's an example including what I gather is your current initialisation - define var dice then define rollOnce() then run rollOnce()
function Dice() {
this.value = 0;
this.kept = false;
this.roll = function() {
if(!this.kept) this.value = Math.floor(Math.random()*6)+1;
};
this.keep = function(id) {
this.kept = true;
document.getElementById(id).className = 'kept';
}
}
var dice1 = new Dice();
var dice2 = new Dice();
var dice3 = new Dice();
function rollOnce() {
dice1.roll();
dice2.roll();
dice3.roll();
document.getElementById("paragraph1").innerHTML =
'<button id="diceOne" class="'+(dice1.kept?'kept':'keep')+'" onclick="dice1.keep(\'diceOne\')">'+dice1.value+'</button> ' +
'<button id="diceTwo" class="'+(dice2.kept?'kept':'keep')+'" onclick="dice2.keep(\'diceTwo\')">'+dice2.value+'</button> ' +
'<button id="diceThree" class="'+(dice3.kept?'kept':'keep')+'" onclick="dice3.keep(\'diceThree\')">'+dice3.value+'</button> ';
}
rollOnce();
I've made it pass an ID to Dice.keep(id) just to have a live update of the DOM element which represents this Object variable.
Some clarification on the classnames since you're a beginner: I used ternary logic operators to quickly perform an IF THEN ELSE
So the part that says dice1.kept?'kept':'keep'
Actually means IF dice1.kept THEN 'kept' ELSE 'keep'
You can put a blank '' instead of 'keep' if you like since I don't think it's being used (but you might use it for CSS). Of course, there is plenty of room for improvement all over this code, but I wanted to keep it as similar to your sample code as possible. In fact, the first thing I would do is probably change the onclick to this: onclick="dice1.keep(this)" and then change your object like:
this.keep = function(button) {
this.kept = true;
button.className = 'kept';
}
http://codepen.io/anon/pen/MyrxyX
Edit: here's a slightly modified version where the Dice() object is agnostic to the DOM but still provides all the relevant data: http://codepen.io/anon/pen/MyrxbB

Accesing function within function JavaScript

I got this piece of code below which is not DRY. What i want to do is to cut it,so everything below var = text would be used only once not twice.
My concept is,to close these two functions in bigger function (e.g. guess()) and keep trimmed correctGuess() and incorrectGuess() within it.
Now here's the question,how can I call such nested function as describe above from outside scope. I was thinking about smth like: guess().correctGuess() which is obviously wrong but I wanted to share a concept.
Additionally, when e.g. correctGuess() would be called, is rest of the commands within our main guess() function would be executed?
function correctGuess(i) {
totalScore++;
questionNumber++;
var text = "Correct!";
var updatePage = ['<div id="answerDiv">' +
'<h1>' + text + '<h1>' +
'<h2>Total Score: ' + totalScore + '</h2></div>'
];
mainContent[html](updatePage);
$('#answerDiv')[fadeIn]("slow");
$('#answerDiv').append('<button id="nextButton">Next Question</button>');
$('#nextButton').on('click', function() {
if (questionNumber == allQuestions.length && totalScore <= 4) {
results()
} else {
question(questionNumber)
}
})
};
var incorrectGuess = function(i) {
totalScore--;
questionNumber++;
var text = "Wrong!";
var updatePage = ['<div id="answerDiv">' +
'<h1>' + text + '<h1>' +
'<h2>Total Score: ' + totalScore + '</h2></div>'
];
mainContent[html](updatePage);
$('#answerDiv')[fadeIn]("slow");
$('#answerDiv').append('<button id="nextButton">Next Question</button>');
$('#nextButton').on('click', function() {
if (questionNumber == allQuestions.length && totalScore <= 4) {
results();
} else {
question(questionNumber);
}
});
};
http://www.w3schools.com/js/js_objects.asp
From your question it seems like you aren't very familiar with object notation. Read up on the above link and then try to create a js "guess" object with 2 member functions. Correct and Incorrect guess.
You need to use the this keyword.
function guess(){
/* do stuff here for guess() */
this.correct = function(){
/* Do stuff for correct */
}
this.wrong = function(){
/* Do stuff for wrong */
}
return this;
}
Because you returned this you can now access the correct() and wrong() functions using:
guess().correct();
// AND
guess().wrong();
Note that whatever code you write inside guess() and outside the two nested functions will also be called every time you call guess().correct() or guess().wrong()
If you do not want any particular code to execute every time they "guess" regardless of right or wrong then I would suggest just storing the correct() and wrong() functions in an object literal.
var guess = {
correct: function(){
// Code for "correct" here
},
wrong: function(){
// Code for "wrong" here
}
}
And then you can access them using
guess.correct();
// AND
guess.wrong();

javascript variables in method use outside the method

Here is my code:
var count = 0;
function retrieveCurrentListProperties() {
clientContext = new SP.ClientContext.get_current();
web = clientContext.get_web();
var list = web.get_lists().getByTitle("Urgent Alerts");
var camlQuery = new SP.CamlQuery();
var q = "<View><Query><Where><Eq><FieldRef Name='End_x0020_Date'/><Value Type='DateTime'><Today/></Value></Eq></Where></Query></View>";
camlQuery.set_viewXml(q);
this.listItems = list.getItems(camlQuery);
clientContext.load(this.listItems);
clientContext.executeQueryAsync(Function.createDelegate(this, this.onCListItemsLoadSuccess),
Function.createDelegate(this, this.onQueryFailed));
}
function onCListItemsLoadSuccess(sender, args) {
var count1 = 0;
var listEnumerator = this.listItems.getEnumerator();
//iterate though all of the items
count1 = this.listItems.get_count();
}
function onQueryFailed(sender, args) {
alert('request failed ' + args.get_message() + '\n' + args.get_stackTrace());
}
count = 1;
This code retrieves a list from sharepoint and then counts how many items are in that list. I need count1 to be used in another part where count=1 but obviously if I did count=count1 it would throw an error.
How can I used count1 in the way that I want
Since SP.ClientContext.executeQueryAsync method executes the current pending request asynchronously on the server, two approaches are commonly used to control the sequential execution of asynchronous calls in SharePoint.
Callbacks
Deferred
With callback approach you declare your function like this
function getData(Success,Error) {
//...
clientContext.executeQueryAsync(function() {
var result = ...
Success(result);
},
Error
);
}
Deferred approach is based on a Promises pattern, please refer this article for a details about the usage of Promises with CSOM.
Example with callback approach
function getItemsCount(listTitle,Success,Error) {
var clientContext = new SP.ClientContext.get_current();
var web = clientContext.get_web();
var list = web.get_lists().getByTitle(listTitle);
var qry = SP.CamlQuery.createAllItemsQuery();
var listItems = list.getItems(qry);
clientContext.load(listItems);
clientContext.executeQueryAsync(function() {
var count = listItems.get_count();
Success(count);
},
Error
);
}
//Usage
getItemsCount('Tasks', function(tasksCount){
//...
console.log('Tasks count:' + tasksCount);
},
function(sender, args) {
console.log('Error:' + args.get_message());
}
);
Recommendations
Avoid global variables
Wrap your code in a scoping function and use variables local to that scoping function, and make your other functions closures within it
(function() { // Begin scoping function
var clientContext; // Global to your code, invisible outside the scoping function
function loadListItems(listTitle) {
// ...
}
})();
Usage of Function.createDelegate
In most cases, there is no need to wrap handlers using Function.createDelegate, instead you could write:
ctx.executeQueryAsync(succeeded,failed);
or
ctx.executeQueryAsync(function(){
//...
},
function(sender,args){
//Error handling goes here
}
);

Speed test: while() cycle vs. jQuery's each() function

I have an array of objects called targets and I want to execute a function on each of those objects. The first method:
targets.each(function() {
if (needScrollbars($(this))) {
wrap($(this), id);
id = id + 1;
}
});
This method gives execution speed of ~125ms. The second method is:
var i=0;
while (targets[i] != undefined) {
if (needScrollbars($(this))) {
wrap($(this), id);
id = id + 1;
}
i = i+1;
}
This second method takes whopping 1385ms to execute and I get my head around that. Does anyone have any idea why a bare bones cycle runs slower than a function which I'm only guessing that's doing (just guessing) a whole lot more than a simple cycle?
Thank you.
They are totally different. The this in the first example is the current target, in the second example this is the "external" this. You should change the second example as:
var i=0;
while (targets[i] != undefined) {
var cur = $(targets[i]);
if (needScrollbars(cur)) {
wrap(cur, id);
id = id + 1;
}
i = i+1;
}
The relevant quote
More importantly, the callback is fired in the context of the current DOM element, so the keyword this refers to the element.
But I don't know why you haven't written as:
for (var i = 0; i < targets.length; i++)
{
var cur = $(targets[i]);
if (needScrollbars(cur)) {
wrap(cur, id);
id = id + 1;
}
}
And in the end the each "method" is easier to comprehend (for me).
Your second method is not functionally equivalent to the first one.
Why? Because it uses this, making it a closure on the global scope. Of course the second method is slower: it continuously shells out jQuery objects made out of global scope. Try that benchmark again with:
var i=0;
while (targets[i] !== undefined) {
var o = $(targets[i]);
if (needScrollbars(o)) {
wrap(o, id);
id++;
}
i++;
}

Categories