google 脚本中的可重用 Google doc Picker - Picker 回调
Reusable Google doc Picker in google scripts - Picker Callback
文档参考:
- Drive file picker v3
- G Suite 对话框:File Open Dialog
SO 参考文献:
- How do I handle the call back using multiple Google File Picker
实现什么?
在 Google 表格脚本 中,我想定义一个文件选取器,returns data
选取的文件,前提是,调用者可以从脚本的另一部分接收到 data
.
问题:
文件选取器作为 html
Modal 对话框启动。搜索了一段时间后,从启动 picker
的脚本 中获取 data
的唯一解决方案是来自 html
脚本代码:
- 将选择器的
callaback
设置为特定功能:picker.setCallback(my_callback)
- 或使用
google.script.run.my_callback
(例如来自按钮 Done
)
...前提是脚本中定义的 my_callback
函数获取 data
.
上面的问题是您不能将同一个 picker
用于多种用途,因为:
my_callback
在 html
脚本 中 已修复
my_callback
不知道最初调用 picker
的目的是什么(即 它应该获取内容吗?,它应该将信息提供给某个未知来电者?)。
一旦它得到 data
,my_callback
不知道如何处理它...除非 my_callback
只绑定到 1 个调用者;这似乎不正确,因为这需要为 picker
定义多个 html
,您可以根据每个原因调用它一次,以便它可以回调到正确的函数。
有什么想法吗?
- 脚本中的全局变量被重新初始化并且不能使用 PropertiesService 来存储
String
以外的值(因此无法通过全局变量存储最终的 picker_callback
)。
google.script.run
不通过将服务器端函数的名称指定为 String
(reference) 来提供调用(它放弃了生成 [=41= 的函数) ] 通过改变回调函数)。
示例代码
code.gs
function ui() {
return SpreadsheetApp.getUi();
}
function onOpen() {
ui().createMenu('ecoPortal Tools')
.addItem('Read a file', 'itemReadFile')
.addItem('Edit a file', 'itemEditFile')
.addToUi();
}
function itemReadFile() {
pickFile(readFile)
}
function itemEditFile() {
pickFile(editFile)
}
function readFile(data) {
/* do some stuff */
}
function editFile(data) {
/* do some stuff */
}
picker.gs
:
function pickFile(callback) {
var html = HtmlService.createHtmlOutputFromFile('picker_dialog.html')
.setWidth(600)
.setHeight(425)
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
// concept (discarded):
callback_set('picker', callback)
ui().showModalDialog(html, 'Select a file');
}
function getOAuthToken() {
DriveApp.getRootFolder();
return ScriptApp.getOAuthToken();
}
// picker callback hub
function pickerCallback(data) {
var callback = callback_get('picker');
callback_set('picker', null);
if (callback) callback.call(data);
}
picker_dialog.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css">
<script>
var DEVELOPER_KEY = '___PICKER_API_KEY_____';
var DIALOG_DIMENSIONS = {width: 600, height: 425};
var pickerApiLoaded = false;
// currently selected files data
var files_data = null;
/**
* Loads the Google Picker API.
*/
function onApiLoad() {
gapi.load('picker', {'callback': function() {
pickerApiLoaded = true;
}});
}
function getOAuthToken() {
console.log("going to call get auth token :)");
google.script.run.withSuccessHandler(createPicker)
.withFailureHandler(showError).getOAuthToken();
}
function createPicker(token) {
console.log("pickerApiLoadded", pickerApiLoaded);
console.log("token", token);
if (pickerApiLoaded && token) {
var picker = new google.picker.PickerBuilder()
.addView(google.picker.ViewId.DOCS)
.enableFeature(google.picker.Feature.NAV_HIDDEN)
.hideTitleBar()
.setOAuthToken(token)
.setDeveloperKey(DEVELOPER_KEY)
.setCallback(pickerCallback)
.setOrigin(google.script.host.origin)
.setSize(DIALOG_DIMENSIONS.width - 2,
DIALOG_DIMENSIONS.height - 2)
.build();
picker.setVisible(true);
} else {
showError('Unable to load the file picker.');
}
}
function pickerCallback(data) {
var action = data[google.picker.Response.ACTION];
if (action == google.picker.Action.PICKED) {
files_data = data;
var doc = data[google.picker.Response.DOCUMENTS][0];
var id = doc[google.picker.Document.ID];
var url = doc[google.picker.Document.URL];
var title = doc[google.picker.Document.NAME];
document.getElementById('result').innerHTML =
'<b>You chose:</b><br>Name: <a href="' + url + '">' + title +
'</a><br>ID: ' + id;
} else if (action == google.picker.Action.CANCEL) {
document.getElementById('result').innerHTML = 'Picker canceled.';
}
}
function showError(message) {
document.getElementById('result').innerHTML = 'Error: ' + message;
}
function closeIt() {
google.script.host.close();
}
function returnSelectedFilesData() {
google.script.run.withSuccessHandler(closeIt).pickerCallback(files_data);
}
</script>
</head>
<body>
<div>
<button onclick='getOAuthToken()'>Select a file</button>
<p id='result'></p>
<button onclick='returnSelectedFilesData()'>Done</button>
</div>
<script src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
</body>
</html>
picker.setCallback(my_callback)
选择器回调不同于:
or use google.script.run.my_callback
前者调用前端函数html,后者调用服务器函数
my_callback cannot know for what purpose the picker was initially called
您可以向服务器发送一个参数:
google.script.run.my_callback("readFile");
在服务器端(code.gs
),
fuction my_callback(command){
if(command === "readFile") Logger.log("Picker called me to readFile");
}
google.script.run does not offer calls by giving the name of the server-side function as String
不正确。点用于访问对象的成员。您可以使用括号表示法将成员作为字符串访问:
google.script.run["my_callback"]();
编辑者Q.ASKER:
在您的情况下,要将 files_data
传递到服务器端:
google.script.run.withSuccessHandler(closeIt)[my_callback](files_data);
现在,要从服务器端设置 my_callback
(字符串变量),您需要使用 templates:
推送它
function pickFile(str_callback) {
var htmlTpl = HtmlService.createTemplateFromFile('picker_dialog.html');
// push variables
htmlTpl.str_callback = str_callback;
var htmlOut = htmlTpl.evaluate()
.setWidth(600)
.setHeight(425)
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
ui().showModalDialog(htmlOut, 'Select a file');
}
您需要对 picker_dialog.html
进行的两项独特更改:
- 添加printing scriptlet设置
my_callback
(<?= ... ?>
)
- 如前所述使用
google.script.run
var my_callback = <?= str_callback? str_callback : 'defaultPickerCallbackToServer' ?>;
/* ... omitted code ... */
function returnSelectedFilesData() {
google.script.run.withSuccessHandler(closeDialog)[my_callback](files_data);
}
现在,当您调用 pickFile
打开前端选择器时,您可以设置一个不同的服务器 callback
来接收包含所选文件的 data
由用户。
文档参考:
- Drive file picker v3
- G Suite 对话框:File Open Dialog
SO 参考文献:
- How do I handle the call back using multiple Google File Picker
实现什么?
在 Google 表格脚本 中,我想定义一个文件选取器,returns data
选取的文件,前提是,调用者可以从脚本的另一部分接收到 data
.
问题:
文件选取器作为 html
Modal 对话框启动。搜索了一段时间后,从启动 picker
的脚本 中获取 data
的唯一解决方案是来自 html
脚本代码:
- 将选择器的
callaback
设置为特定功能:picker.setCallback(my_callback)
- 或使用
google.script.run.my_callback
(例如来自按钮Done
)
...前提是脚本中定义的 my_callback
函数获取 data
.
上面的问题是您不能将同一个 picker
用于多种用途,因为:
my_callback
在html
脚本 中 已修复
my_callback
不知道最初调用picker
的目的是什么(即 它应该获取内容吗?,它应该将信息提供给某个未知来电者?)。
一旦它得到 data
,my_callback
不知道如何处理它...除非 my_callback
只绑定到 1 个调用者;这似乎不正确,因为这需要为 picker
定义多个 html
,您可以根据每个原因调用它一次,以便它可以回调到正确的函数。
有什么想法吗?
- 脚本中的全局变量被重新初始化并且不能使用 PropertiesService 来存储
String
以外的值(因此无法通过全局变量存储最终的picker_callback
)。 google.script.run
不通过将服务器端函数的名称指定为String
(reference) 来提供调用(它放弃了生成 [=41= 的函数) ] 通过改变回调函数)。
示例代码
code.gs
function ui() {
return SpreadsheetApp.getUi();
}
function onOpen() {
ui().createMenu('ecoPortal Tools')
.addItem('Read a file', 'itemReadFile')
.addItem('Edit a file', 'itemEditFile')
.addToUi();
}
function itemReadFile() {
pickFile(readFile)
}
function itemEditFile() {
pickFile(editFile)
}
function readFile(data) {
/* do some stuff */
}
function editFile(data) {
/* do some stuff */
}
picker.gs
:
function pickFile(callback) {
var html = HtmlService.createHtmlOutputFromFile('picker_dialog.html')
.setWidth(600)
.setHeight(425)
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
// concept (discarded):
callback_set('picker', callback)
ui().showModalDialog(html, 'Select a file');
}
function getOAuthToken() {
DriveApp.getRootFolder();
return ScriptApp.getOAuthToken();
}
// picker callback hub
function pickerCallback(data) {
var callback = callback_get('picker');
callback_set('picker', null);
if (callback) callback.call(data);
}
picker_dialog.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css">
<script>
var DEVELOPER_KEY = '___PICKER_API_KEY_____';
var DIALOG_DIMENSIONS = {width: 600, height: 425};
var pickerApiLoaded = false;
// currently selected files data
var files_data = null;
/**
* Loads the Google Picker API.
*/
function onApiLoad() {
gapi.load('picker', {'callback': function() {
pickerApiLoaded = true;
}});
}
function getOAuthToken() {
console.log("going to call get auth token :)");
google.script.run.withSuccessHandler(createPicker)
.withFailureHandler(showError).getOAuthToken();
}
function createPicker(token) {
console.log("pickerApiLoadded", pickerApiLoaded);
console.log("token", token);
if (pickerApiLoaded && token) {
var picker = new google.picker.PickerBuilder()
.addView(google.picker.ViewId.DOCS)
.enableFeature(google.picker.Feature.NAV_HIDDEN)
.hideTitleBar()
.setOAuthToken(token)
.setDeveloperKey(DEVELOPER_KEY)
.setCallback(pickerCallback)
.setOrigin(google.script.host.origin)
.setSize(DIALOG_DIMENSIONS.width - 2,
DIALOG_DIMENSIONS.height - 2)
.build();
picker.setVisible(true);
} else {
showError('Unable to load the file picker.');
}
}
function pickerCallback(data) {
var action = data[google.picker.Response.ACTION];
if (action == google.picker.Action.PICKED) {
files_data = data;
var doc = data[google.picker.Response.DOCUMENTS][0];
var id = doc[google.picker.Document.ID];
var url = doc[google.picker.Document.URL];
var title = doc[google.picker.Document.NAME];
document.getElementById('result').innerHTML =
'<b>You chose:</b><br>Name: <a href="' + url + '">' + title +
'</a><br>ID: ' + id;
} else if (action == google.picker.Action.CANCEL) {
document.getElementById('result').innerHTML = 'Picker canceled.';
}
}
function showError(message) {
document.getElementById('result').innerHTML = 'Error: ' + message;
}
function closeIt() {
google.script.host.close();
}
function returnSelectedFilesData() {
google.script.run.withSuccessHandler(closeIt).pickerCallback(files_data);
}
</script>
</head>
<body>
<div>
<button onclick='getOAuthToken()'>Select a file</button>
<p id='result'></p>
<button onclick='returnSelectedFilesData()'>Done</button>
</div>
<script src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
</body>
</html>
picker.setCallback(my_callback)
选择器回调不同于:
or use google.script.run.my_callback
前者调用前端函数html,后者调用服务器函数
my_callback cannot know for what purpose the picker was initially called
您可以向服务器发送一个参数:
google.script.run.my_callback("readFile");
在服务器端(code.gs
),
fuction my_callback(command){
if(command === "readFile") Logger.log("Picker called me to readFile");
}
google.script.run does not offer calls by giving the name of the server-side function as String
不正确。点用于访问对象的成员。您可以使用括号表示法将成员作为字符串访问:
google.script.run["my_callback"]();
编辑者Q.ASKER:
在您的情况下,要将 files_data
传递到服务器端:
google.script.run.withSuccessHandler(closeIt)[my_callback](files_data);
现在,要从服务器端设置 my_callback
(字符串变量),您需要使用 templates:
function pickFile(str_callback) {
var htmlTpl = HtmlService.createTemplateFromFile('picker_dialog.html');
// push variables
htmlTpl.str_callback = str_callback;
var htmlOut = htmlTpl.evaluate()
.setWidth(600)
.setHeight(425)
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
ui().showModalDialog(htmlOut, 'Select a file');
}
您需要对 picker_dialog.html
进行的两项独特更改:
- 添加printing scriptlet设置
my_callback
(<?= ... ?>
) - 如前所述使用
google.script.run
var my_callback = <?= str_callback? str_callback : 'defaultPickerCallbackToServer' ?>;
/* ... omitted code ... */
function returnSelectedFilesData() {
google.script.run.withSuccessHandler(closeDialog)[my_callback](files_data);
}
现在,当您调用 pickFile
打开前端选择器时,您可以设置一个不同的服务器 callback
来接收包含所选文件的 data
由用户。