javascript variables in method use outside the method - javascript

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
}
);

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).

ext js - create custom function with passed parameters

Just a warning, this is my first ExtJS project.
I have two stores loaded from a webserver successfully.
Store containing positions
Store containing marketData
I've created a third store to hold all of my results.
Now I want to go through each position, find the market data record associated, and run a simple calculation.
I have done this successfully all on the event of clicking a button, but I want to separate out the function of doing the actual calculation... passing in parameters.
For now just to get the concept working I created a function called 'sayHello', but I am getting an error stating... ReferenceError: sayHello is not defined.
Can someone point out what I am doing wrong to create this custom function?
Thanks!
my controller...
Ext.define('ExtApplication1.view.clientdetails.clientdetailsController', {
extend: 'Ext.app.ViewController',
alias: 'controller.clientdetails-clientdetails',
onClickCalculate: function () {
console.log('calculation button was hit');
var targetGrid = Ext.getCmp('positionsGridID');
var positionsStore = targetGrid.store;
var marketDataGrid = Ext.getCmp('marketsGridID');
var marketDataStore = marketDataGrid.store;
var calculatedPositionsDataGrid = Ext.getCmp('calculatedPositionsGridID');
var calculatedPositionsDataStore = calculatedPositionsDataGrid.store;
console.log(calculatedPositionsDataStore);
positionsStore.each(function (record) {
console.log('the details for the whole position');
console.log(record);
var bbSymbol = record.get('BBSymbol');
var singleRecord;
marketDataStore.each(function (record) {
var cycleBBSymbol = record.get('BBSymbol');
if (cycleBBSymbol === bbSymbol){
singleRecord = record;
return false;
}
});
console.log('position I am evaluateing is ' + bbSymbol);
console.log('market data found for ' + singleRecord.get('BBSymbol'));
console.log(singleRecord);
//debugger;
var lastPrice = singleRecord.get('Last_Price');
var settle = singleRecord.get('Px_Settle');
var qty = record.get('Quantity');
var marketName = record.get('Description');
var pnl = (lastPrice - settle) * qty;
console.log(pnl);
calculatedPositionsDataStore.add({
BBSymbol: bbSymbol,
Description: marketName,
Quantity: qty,
CalcPLSett: pnl
});
sayHello(singleRecord);
}, this);
},
sayHello: function (singleRecord) {
alert('hello');
alert(singleRecord);
}
});
You get this error because you're out of the scope of the ViewController.
In
positionsStore.each(function (record) { ...}
You are in the store scope, but the sayHello function is in the ViewController scope.
Assign the ViewController's scope to a variable, should solve your problem:
onClickCalculate: function () {
console.log('calculation button was hit');
var me = this; //NEW LINE
var targetGrid = Ext.getCmp('positionsGridID');
var positionsStore = targetGrid.store;
And then use it in the positionsStore.each function :
me.sayHello(singleRecord)

Waiting for a call function to finish before iterating forward

I am in a bit of a bind here regarding a code I have to write.
At the moment I am trying to iterate through an array of strings. After a certain text is found, if a value is true, the value is bound to a temporaryObject.img. After that a call has to be made to the server in order for some information to be retrieved and to be bound to the same temporaryObject as a second property (temporaryObject.url). Unfortunately, the call is resolved probably after the iteration is done because it does not add this add the url from the call to the server in the Object. The libraries I am using are angularjs and jquery.
Here is a part of the code I wrote.
$scope.Data = [];
var strArray = ["img1", "link", "img2", "link2", "img3", "link3"];
var re1 = /<img/;
for(var i = 0 ; i < strArray.length ; i++) {
var tempObject = {};
var test = re1.test(strArray[i]);
if (test){
tempObject.img = strArray[i + 1];
myAngularService.getServerInformation().then(function(result){
tempObject.url = result;
$scope.Data.push(tempObject);
}
}
Unfortunately, the information from the server does not arrive in the same time as the iteration is done or something goes wrong since $scope.Data will not contain the url requested from the server. Any suggestions as how to use maybe a promise for this kind of code would be appreciated. I tried searching every possible solution but with no avail.
Javascript is function-scoped. In other words:
if (true) {
var testing = 5;
}
console.log(testing); // Logs 5
Applying this to your example, tempObject in your .then callback function are all referencing the same variable!
One solution is to use an Immediately-Invoked Function Expression (IIFE) inside your for statement to create a new scope:
for(var i = 0 ; i < strArray.length ; i++) {
(function() {
var tempObject = {}; // Is now declared within the new function
/* ... code here ... */
})();
}
Another solution is to simply use $.each (or angular.forEach):
$.each(strArray, function(index, value) {
var tempObject = {}; // Is now declared within the new function
/* ... code here ... */
});
Regarding waiting for all your calls to finish, one solution is to store your promises in an array use angular's $q service:
$scope.Data = [];
var promises = [];
var strArray = ["img1", "link", "img2", "link2", "img3", "link3"];
var re1 = /img/;
$.each(strArray, function (index, value) {
var tempObject = {};
var test = re1.test(value);
if (test) {
tempObject.img = strArray[index + 1];
var myPromise = myAngularService.getServerInformation();
myPromise.then(function (result) {
tempObject.url = result;
$scope.Data.push(tempObject);
}
promises.push(myPromise);
}
})
$q.all(promises).then(function() {
// All calls will have finished
console.log($scope.Data);
}, function() {
// At least 1 call failed!
console.log("an error occurred!");
});

How can I get a bool value, after I check if current user in sharepoint group in javascript?

JS Function
I write a function to check if user in sharepoint group in javascript
function IsCurrentUserMemberOfGroup(groupName, OnComplete) {
var currentContext = new SP.ClientContext.get_current();
var currentWeb = currentContext.get_web();
var currentUser = currentContext.get_web().get_currentUser();
currentContext.load(currentUser);
var allGroups = currentWeb.get_siteGroups();
currentContext.load(allGroups);
currentContext.load(allGroups, 'Include(Users)');
currentContext.executeQueryAsync(OnSuccess, OnFailure);
function OnSuccess(sender, args) {
var userInGroup = false;
var groupEnumerator = allGroups.getEnumerator();
while (groupEnumerator.moveNext()) {
var oGroup = groupEnumerator.get_current();
if (groupName == oGroup.get_title()) {
var allUsers = oGroup.get_users();
var userEnumerator = allUsers.getEnumerator();
while (userEnumerator.moveNext()) {
var oUser = userEnumerator.get_current();
if (oUser.get_id() == currentUser.get_id()) {
userInGroup = true;
break;
}
}
}
}
OnComplete(userInGroup);
}
function OnFailure(sender, args) {
OnComplete(false);
} }
Usage
I use it in another function, wish to get the bool value of OnComplete and return it.
function SetButtonPermission() {
var isInGroup;
IsCurrentUserMemberOfGroup("Global", function(isCurrentUserInGroup) {
isInGroup = isCurrentUserInGroup;
});
return isInGroup; }
Question
It seems like I cannot get the bool isCurrentUserInGroup because it alert "isInGroup is undetified".
So How Can I Get The bool value ?
Similar to the answer provided here, when you're dealing with asynchronous function calls and callbacks, you'll be better off injecting data/logic into your callback function instead of returning data out from it.
The alternative is to push the "return" data into a global variable, or at least to a variable accessible within the same scope as the callback's execution, and delay execution of dependent logic until after the callback has executed.
You might want to look into JavaScript promises to see how script authors typically handle asynchronous code.

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

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​​​​​

Categories