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).
Related
var getFileName = null
$('#fileInput').change(function() {
var props = $('#fileInput').prop('files'),
file = props[0]
getFileName = "" + file.name
console.log("inside function: " + getFileName)
})
// selected file
console.log("outside function: " + getFileName);
The variable is changing correctly inside the function but i can't get the changed value outside the function. I think i overlook something but i can't fix it at the moment. π
Like mentioned in the comments the lines are not executed in the order they are noted cause of callbacks.
You should probably do this
var getFileName = null; // global variable
$('#fileInput').change(function() {
var props = $('#fileInput').prop('files'),
file = props[0],
getFileName = "" + file.name
;
console.log("inside function: " + getFileName); // log in callback
checkOutside();
})
function checkOutside(){
// output global variable after its changed in callback of "change"-event
console.log("outside function: " + getFileName);
}
I'm writing an interface for user settings in a Chrome extension. Here's how I define the settings:
function Setting(label, type, defaultData) {
this.label = label;
this.type = 'checkbox';
this.defaultData = defaultData;
}
var Settings = {};
Settings['setting-one'] = new Setting('Setting one', 'checkbox', 'true');
Settings['setting-two'] = new Setting('Setting two', 'checkbox', 'true');
Here's how I set the default value for each setting:
function setDefaultSetting(setting, defaultValue) {
chrome.storage.sync.get(setting, function(results) {
if (!results[setting]) {
// If the setting has not yet been defined, define it now
var dataToSave = {};
dataToSave[setting] = defaultValue;
chrome.storage.sync.set(dataToSave, function() {
debugMsg('set `' + setting + '` to ' + defaultValue);
});
}
});
}
for (var setting in Settings) {
if (Settings.hasOwnProperty(setting)) {
var s = Settings[setting];
if (s.type == 'checkbox') {
setDefaultSetting(setting, s.defaultData);
}
}
}
So far, so good. Now I want to print the list of settings as checkboxes. Here's what I've tried:
function printSettingsModal() {
var output += '<form>';
for (var setting in Settings) {
if (Settings.hasOwnProperty(setting)) {
var s = Settings[setting];
if (s.type == 'checkbox') {
chrome.storage.sync.get(setting, function(results) {
output += '<p><input id="setting-' + setting + '" type="checkbox"';
if (results[setting] == 'true') { output += ' checked="checked"'; }
output += '><label for="setting-' + setting + '">' + s.name + '</label></p>';
});
}
}
}
output += '</form>';
return output;
}
This doesn't work because chrome.storage.sync.get() is asynchronous. How can I loop through my array of settings and retrieve the associated chrome.storage data for each setting?
You don't actually have to do that sequentially, or even set defaults explicitly beforehand.
chrome.storage.local.get has 3 forms for its first argument:
"key" (string): retrieves just one key, as you're doing
["key1", "key2"] (array): retrieves all the values for keys in the array
{ key1: default1, key2: default2 } (dictionary object): retrieves all the values for keys specified, returning the provided default value if it's not in storage
You can collect all the settings you want to retrieve and get them in one operation.
A simple way to do it is to use a recursive function, add each option inside the callback of the chrome.storage.sync.get() method, and call the printSettingsModal() function increasing the index. To do this you'll need an array structure to at least store the name of the settings so you can iterate over it, you can edit your Settings object and add the method add() that will do this for you. You can then call the chrome.storage.sync.get() method recursively and call a callback when finished.
So, first, here is your new Settings object:
function Setting(label, type, defaultData) {
this.label = label;
this.type = 'checkbox';
this.defaultData = defaultData;
}
function Settings() {
var _this = this;
this.list = [];
this.add = function(key, obj) {
this.list.push(key);
_this[key] = obj;
}
}
var mySettings = new Settings();
mySettings.add('setting-one', new Setting('Setting one', 'checkbox', 'true'));
mySettings.add('setting-two', new Setting('Setting two', 'checkbox', 'true'));
Now you will have something like this:
console.log(mySettings);
Settings {list: Array[2], add: function, setting-one: Setting, setting-two: Setting}
Here comes the recursive function, you will need to:
Initialize the output as "<form>" at the beginning.
Add all the settings in the intermediate steps.
Finalize the output adding "</form>" at the end.
Call a callback with the output so you can use it properly.
Here is an example of what your function will look like using recursion:
function printSettingsModal(index, output, callback) {
var s = mySettings[mySettings.list[index]];
if (index === 0) {
// first element is going to be created
output = "<form>";
} else if (index > mySettings.list.length-1) {
// the last setting has been added
// call the callback
callback(options);
}
if (s.type == 'checkbox') {
chrome.storage.sync.get(setting, function(results) {
output += '<p><input id="setting-' + setting + '" type="checkbox"';
if (results[setting] == 'true') { output += ' checked="checked"'; }
output += '><label for="setting-' + setting + '">' + s.name + '</label></p>';
if (index === mySettings.list.length-1) {
// last element has been created
output += '</form>';
printSettingsModal(++index, output, callback);
}
});
}
}
Now you just need to define a callback and call it:
function myCallback(options) {
// do something with the option string
// like:
document.getElementById('form-container').innerHTML = options;
}
printSettingsModal(0, null, myCallback);
If I wrote all correctly, this will work for you. Hope it helped.
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βββββ
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
}
);
I'm having an issue with a helper function inside my Backbon.js View. When it's run, it dies with the following error message about the first line of the "addCalc" function:
TypeError: this.getCalcValue is not a function
It's really puzzling because in the "initialize" function defined just above, all the functions seem to be defined. It feels like I'm calling the sibling method wrong, and the "initialize" method is an exception where "this" can be used to reference the object.
Is there something wrong/missing with the following code, or something I missed with the backbone documentation?
CalcView = Backbone.View.extend({
el: $("#calcView"),
initialize: function () {
this.resetCalc();
},
addCalc: function (model) {
var cost = this.getCalcValue(model.get('currentCost'));
var custom = this.getCalcValue(model.get('customProgram'));
var variables = { id: model.get('id'),
category: model.get('category'),
shortDesc: model.get('shortDescription'),
description: model.get('description'),
currentCost: cost,
customProgram: custom,
};
var template = _.template($('#calc_template').html(), variables);
$("#calc_payload").append(template);
},
resetCalc: function(models) {
$("#calc_payload tr").remove();
},
removeCalc: function(model){
$("#calc_payload #" + model.get('id')).remove();
},
updateCalcs: function(model) {
var cost = model.get('currentCost');
var custom = model.get('customProgram');
$("#" + model.get("id") + " .currentCost").text(this.getCalcValue(cost));
$("#" + model.get("id") + " .customProgram").text(this.getCalcValue(custom));
/*var currentCostSum = 0;
var customProgramSum = 0;
$("#calc_payload .currentCost").each(function() {
var temp = Number(($(this).text()).replace(/[^0-9\.]+/g, ""));
if (!isNaN(temp))
currentCostSum += temp;
});
$("#calc_payload .customProgram").each(function() {
var temp = Number(($(this).text()).replace(/[^0-9\.]+/g, ""));
if (!isNaN(temp))
customProgramSum += temp;
});
$("#calc_footer .currentCost").text("$" + ((currentCostSum == 0) ? " -- " : CurrencyFormatted(currentCostSum.toFixed(2))));
$("#calc_footer .customProgram").text("$" + ((customProgramSum == 0) ? " -- " : CurrencyFormatted(customProgramSum.toFixed(2))));*/
},
getCalcValue: function(value) {
if (typeof value == 'string' || value instanceof String)
return value.toString();
else if (isNaN(value))
return "$ -- ";
else
return "$" + value.toFixed(2);
},
});
The code that executes the "addCalc" function is driven by a backbone collection. Basically, when the collection is added to, the CalcView.addCalc is called
Calculations = Backbone.Collection.extend({
model: Calculation,
//This is our Friends collection and holds our Friend models
initialize: function (models, options) {
this.on("add", options.onAdd);
this.on("remove", options.onRemove);
this.on("reset", options.onReset);
//Listen for new additions to the collection and call a view function if so
}
});
//This is where addCalc is used.
var calcview = new CalcView();
var calc_collection = new Calculations( null, {
onAdd: calcview.addCalc,
onRemove: calcview.removeCalc,
onReset: calcview.resetCalc
});
In your initialize function add this line of code:
_.bindAll(this,'addCalc');
This will bind this to be your CalcView for the addCalc function. You can put multiple comma separated method names in there if you need to bind more than one function...
See Underscore's documentation on it here.
When you bind events on collection you can send the context as third argument. Try sending one more option property as your calcview and pass it as context.
this.on("add", options.onAdd, options.calcview);
this.on("remove", options.onRemove, options.calcview);
this.on("reset", options.onReset, options.calcview);