I'm working with Sharepoint and I have an app that creates lists in the site collection (not the app!). Creating the list isn't a problem. I wan't to create 15 columns and add them to the default view. I prepared a "config" with the information for the list and its fields.
我正在与Sharepoint合作,我有一个应用程序可以在站点集合中创建列表(而不是应用程序!)创建列表不是问题。我不想创建15列并将它们添加到默认视图中。我准备了一个“配置”,其中包含列表及其字段的信息。
var ChangeRequestLogListConfig = {
listName: 'ChangeRequestLog',
fields: [
{ 'FieldTypeKind': 8, 'Title': 'STC Relevant', 'InternalName': 'STCrelevant', 'StaticName': 'STCrelevant' },
{ 'FieldTypeKind': 3, 'Title': 'Description/Reason', 'InternalName': 'DescriptionReason', 'StaticName': 'DescriptionReason' },
{ 'FieldTypeKind': 6, 'Title': 'Source of Change', 'InternalName': 'SourceOfChange', 'StaticName': 'SourceOfChange', 'Choices': { 'results': ['customer', 'internal'] } },
//12 more objects like the above
]
}
There we come to the problem: with the rest resources .../ViewFields/AddViewField
and .../fields
(with http post to create new ones) only support one parameter which means I have to do 2x15 ajax calls.
问题来了:有了其余的资源……/ ViewFields / AddViewField和…/字段(使用http post创建新字段)只支持一个参数,这意味着我必须执行2x15 ajax调用。
What's the proper approach to doing all these 30 operations and then do a final callback? Of course I know how to loop through the array of fields, I simply have no clue how to build that final callback the proper way.
做这30个操作然后做最后一次回调的正确方法是什么?当然,我知道如何循环遍历字段数组,我只是不知道如何正确地构建最终回调。
Update / Follow-up
更新/跟踪
With the help of a few answers I managed to build the following code:
在一些答案的帮助下,我成功地构建了以下代码:
var spExecutor = new SP.RequestExecutor(_spPageContextInfo.siteAbsoluteUrl);
var requestUrl = _spPageContextInfo.siteAbsoluteUrl + "/_api/SP.AppContextSite(@target)/web/Lists/getbytitle('" + listConfig.listName + "')/fields?@target='" + hostWebUrl + "'";
//map field configs to promises
var promises = listConfig.fields.map(fieldConfig => {
return spExecutor.executeAsync({
url: requestUrl,
method: "POST",
body: JSON.stringify(fieldConfig),
headers: {
"accept": "application/json;odata=verbose",
"content-type": "application/json; odata=verbose"
},
error: err => console.log(err)
});
});
//Wait for all calls to be done
$.when.apply($, promises).then(callbackFn);
As you probably already saw, I'm not using $.ajax
but the SP.RequestExecutor
. The problem: This doesn't return promises the same way as jQuery's ajax. This results in timing problems (--> 15 Calls at once, browsers mostly only support 4 parallel calls). The code above works, if I set a breakpoint and wait 1-2 seconds between the calls it creates all fields as expected.
正如您可能已经看到的,我没有使用$。ajax但SP.RequestExecutor。问题是:返回的承诺与jQuery的ajax不同。这导致了时间问题(- > 15调用一次,浏览器大多只支持4个并行调用)。上面的代码可以工作,如果我设置一个断点,并在调用之间等待1-2秒,它将按预期创建所有字段。
My follow-up question: How can I wait for the completion of the first call to init the second, then the third and so on? I'm not sure if I'm using the promises the right way.
我接下来要问的问题是:如何等待第一个调用的完成来初始化第二个调用,然后是第三个调用等等?我不确定我是否用了正确的方法。
2 个解决方案
#1
2
Use .map
to convert each field into a Promise
:
使用.map将每个字段转换为一个承诺:
var promises = ChangeRequestLogListConfig.fields.map(
item => $.ajax( ... )
);
and then $.when
to wait for them all to complete:
然后美元。何时等待它们全部完成:
$.when.apply($, promises).then( ... );
Note that this will start as many AJAX requests in parallel as your browser will permit - typically four.
注意,这将在浏览器允许的范围内并行启动尽可能多的AJAX请求——通常是4个。
EDIT since you've said you actually want the AJAX queries to run in series (to avoid server 409 errors) my version of your addFieldsToList
function would look like this:
编辑,因为您已经说过您实际上希望AJAX查询以系列方式运行(为了避免服务器409错误),我版本的addFieldsToList函数将如下所示:
function addFieldsToList(listConfig) {
return listConfig.fields.reduce(function(promise, field) {
return promise.then(executeRequest(field, listConfig.listName));
}, Promise.resolve(null));
}
avoiding passing the callback, since the return value of this function will itself be a Promise
that you can chain:
避免传递回调,因为这个函数的返回值本身就是一个承诺,您可以链:
addFieldsToList(myConfig).then(callback);
#2
0
Finally I ended up doing this:
最后我这样做了:
//Promise returning request
function executeRequest(fieldConfig, listName) {
return function () {
return new Promise(function (resolve, reject) {
var requestUrl = _spPageContextInfo.siteAbsoluteUrl + "/_api/SP.AppContextSite(@target)/web/Lists/getbytitle('" + listName + "')/fields?@target='" + riskapp.utils.getSpHostUrl() + "'";
var spExecutor = new SP.RequestExecutor(_spPageContextInfo.siteAbsoluteUrl);
spExecutor.executeAsync({
url: requestUrl,
method: "POST",
body: JSON.stringify(fieldConfig),
headers: {
"accept": "application/json;odata=verbose",
"content-type": "application/json; odata=verbose"
},
success: resolve,
error: reject
});
});
};
}
//Add fields to list according to config
function addFieldsToList(listConfig, callback) {
//Prepare empty/resolved promise to iterate later
var promise = Promise.resolve(null);
//Loop through every field
$.each(listConfig.fields, function (i, field) {
promise = promise.then(executeRequest(listConfig[field], listConfig.listName));
});
//execute callback when all fields are created
promise.then(callback);
}
This executes all the calls in an order, not simultaneously.
这将按顺序执行所有调用,而不是同时执行。
#1
2
Use .map
to convert each field into a Promise
:
使用.map将每个字段转换为一个承诺:
var promises = ChangeRequestLogListConfig.fields.map(
item => $.ajax( ... )
);
and then $.when
to wait for them all to complete:
然后美元。何时等待它们全部完成:
$.when.apply($, promises).then( ... );
Note that this will start as many AJAX requests in parallel as your browser will permit - typically four.
注意,这将在浏览器允许的范围内并行启动尽可能多的AJAX请求——通常是4个。
EDIT since you've said you actually want the AJAX queries to run in series (to avoid server 409 errors) my version of your addFieldsToList
function would look like this:
编辑,因为您已经说过您实际上希望AJAX查询以系列方式运行(为了避免服务器409错误),我版本的addFieldsToList函数将如下所示:
function addFieldsToList(listConfig) {
return listConfig.fields.reduce(function(promise, field) {
return promise.then(executeRequest(field, listConfig.listName));
}, Promise.resolve(null));
}
avoiding passing the callback, since the return value of this function will itself be a Promise
that you can chain:
避免传递回调,因为这个函数的返回值本身就是一个承诺,您可以链:
addFieldsToList(myConfig).then(callback);
#2
0
Finally I ended up doing this:
最后我这样做了:
//Promise returning request
function executeRequest(fieldConfig, listName) {
return function () {
return new Promise(function (resolve, reject) {
var requestUrl = _spPageContextInfo.siteAbsoluteUrl + "/_api/SP.AppContextSite(@target)/web/Lists/getbytitle('" + listName + "')/fields?@target='" + riskapp.utils.getSpHostUrl() + "'";
var spExecutor = new SP.RequestExecutor(_spPageContextInfo.siteAbsoluteUrl);
spExecutor.executeAsync({
url: requestUrl,
method: "POST",
body: JSON.stringify(fieldConfig),
headers: {
"accept": "application/json;odata=verbose",
"content-type": "application/json; odata=verbose"
},
success: resolve,
error: reject
});
});
};
}
//Add fields to list according to config
function addFieldsToList(listConfig, callback) {
//Prepare empty/resolved promise to iterate later
var promise = Promise.resolve(null);
//Loop through every field
$.each(listConfig.fields, function (i, field) {
promise = promise.then(executeRequest(listConfig[field], listConfig.listName));
});
//execute callback when all fields are created
promise.then(callback);
}
This executes all the calls in an order, not simultaneously.
这将按顺序执行所有调用,而不是同时执行。