使用 App Script 在多个电子表格上一次执行一个功能
Use App Script to carry out one function, once, on many spreadsheets
我想在 Google 个工作表上运行一个 Google 脚本来规范化单元格范围。或者更准确地说,我希望(技术含量较低的)用户能够执行此操作。我似乎找不到合理的工作流程。
我能看到的选项有:
Copy/paste 脚本作为每个电子表格的绑定脚本
这很麻烦,因为脚本有很多副本,通常不会再次运行,而且有很多开销和点击为每个脚本安装宏。
使用图书馆
我可以将代码主体放入库中,然后将 copy/paste 设为存根(就像已接受的答案 here 一样)。
然而,这对用户体验来说仍然很糟糕,而且各种报告称库处理起来很麻烦。
制作一个插件
“正确的方法”似乎是创建一个用户可以为每个电子表格启用的插件。然而,附加组件似乎仍处于“开发者预览”模式,授权周期不确定且可能很慢。 Google 还希望 该脚本已经过多个活跃用户的测试。 这很难 - 当我彻底测试它时,工作基本上就完成了.在不发布附加组件的情况下如何测试它?
其他选项?
是否有其他方法,也许使用未绑定脚本?由于需要一些用户输入(电子表格中的范围等),不可能运行一次单个脚本并让它遍历所有电子表格。
有没有一种方法可以让用户安装一个未绑定的脚本,运行它,然后它会询问要在哪个电子表格上运行它?
openByUrl()
非常接近,但它实际上并没有打开电子表格用户界面,所以我无法使用 getActiveRange()
等功能
如果它是相关的,这里是脚本:
/*function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Normalize')
.addItem('Normalize Crosstab', 'normalizeCrossTab')
.addToUi();
}*/
function onOpen() {
var ss = SpreadsheetApp.getActive();
var items = [
{name: 'Normalize Crosstab', functionName: 'normalizeCrosstab'},
];
ss.addMenu('Normalize', items);
}
/* Converts crosstab format to normalized form. Given columns abcDE, the user puts the cursor somewhere in column D.
The result is a new sheet, NormalizedResult, like this:
a b c Field Value
a1 b1 c1 D D1
a1 b1 c1 E E1
a2 b2 c2 D D2
a2 b2 c2 E E2
...
*/
function normalizeCrosstab() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var firstDataCol = SpreadsheetApp.getActiveRange().getColumn();
var dataCols = values[0].slice(firstDataCol-1);
if (Browser.msgBox("This will create a new sheet, NormalizedResult. Place your cursor is in the first data column.\n\n" +
"These will be your data columns: " + dataCols,Browser.Buttons.OK_CANCEL) == "cancel") {
return;
}
var resultssheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("NormalizedResult");
if (resultssheet != null) {
SpreadsheetApp.getActive().deleteSheet(resultssheet);
}
var header = values[0].slice(0, firstDataCol - 1);
var newRows = [];
header.push("Field");
header.push("Value");
newRows.push(header);
for (var i = 1; i <= numRows - 1; i++) {
var row = values[i];
for (var datacol = 0; datacol < dataCols.length; datacol ++) {
newRow = row.slice(0, firstDataCol - 1); // copy repeating portion of each row
newRow.push(values[0][firstDataCol - 1 + datacol]); // field name
newRow.push(values[i][firstDataCol - 1 + datacol]); // field value
newRows.push(newRow);
}
}
var newSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("NormalizedResult");
var r = newSheet.getRange(1,1,newRows.length, header.length);
r.setValues(newRows);
};
您可以检索用户提供 URL 的 sheet,将它们显示在简单的 HTML 中,一个 sheet 在另一个下面,并附加一个按钮列,这将为该行调用 normalizeCrosstab()。这是已发布的HTML解决方案,任何人无需登录即可使用。
如果有定义数量的 sheet,您也可以在 HTML 中使用名称旁边的按钮生成它们,它会生成 TABLE HTML.
或者使用图书馆,我怀疑你有什么需要但不能做的,那个答案很旧 (12')。
我正在使用库并且没有遇到任何问题,对所有事情都非常方便,所有 sheets 必须具有这 3 个函数才能工作,就好像脚本在 sheet 本身中一样:
function onOpen() {
library.onInitialize();
}
function onEdit(celEd) {
library.onMakeEdit(celEd);
}
function libraryFuncs( funcName, args ){ // Needed for sideBars to use library functions
if(args)
args = args.split("\!|"); // Predefined separation of args
else
args = [];
return library[ funcao ]().apply(this, args);
}
第一个问题是:"Who is the owner of all these sheets?"如果您是所有这些工作表的所有者,那么您有权远程访问它们。如果您不拥有它们,则所有者需要共享并授予试图修改其文件的任何代码的编辑权限。
如果您拥有所有电子表格,则可以创建一个独立应用程序,从一个中心点进行所有处理。然后,您可以将 Stand Alone App 的 link 通过电子邮件发送给每个人,或者让每个用户在他们的电子表格中输入一个 link 到 Stand Alone App。正如您所提到的,对于该选项,您将无法使用 getActiveSheet()
.
等方法
无论您使用什么选项,您都需要让人们向他们的电子表格添加内容,或者创建一些新的集中式界面。最适合您的选择可能归结为所有权和设置权限。
我猜如果电子表格的用户是所有者,并且不想授予您许可,他们将需要使用您的前三个选项之一。我会先从图书馆开始。
如果您可以轻松获取电子表格的文件 ID,则可以创建一个将用户与文件 ID 相匹配的对象。
var objUserToFileID = {"user1":"abc34ciu89384u", "user2":"FileID_Two", "user3":"FileID_Three"};
然后让用户从列表中选择他们的名字,(下拉列表)然后 运行 代码。那是针对独立应用程序的。当然,您需要弄清楚如果用户为其他人的电子表格选择文件 ID 会发生什么情况。那么您需要有一种方法来确定应用程序的用户是谁。
我想在 Google 个工作表上运行一个 Google 脚本来规范化单元格范围。或者更准确地说,我希望(技术含量较低的)用户能够执行此操作。我似乎找不到合理的工作流程。
我能看到的选项有:
Copy/paste 脚本作为每个电子表格的绑定脚本
这很麻烦,因为脚本有很多副本,通常不会再次运行,而且有很多开销和点击为每个脚本安装宏。
使用图书馆
我可以将代码主体放入库中,然后将 copy/paste 设为存根(就像已接受的答案 here 一样)。
然而,这对用户体验来说仍然很糟糕,而且各种报告称库处理起来很麻烦。
制作一个插件
“正确的方法”似乎是创建一个用户可以为每个电子表格启用的插件。然而,附加组件似乎仍处于“开发者预览”模式,授权周期不确定且可能很慢。 Google 还希望 该脚本已经过多个活跃用户的测试。 这很难 - 当我彻底测试它时,工作基本上就完成了.在不发布附加组件的情况下如何测试它?
其他选项?
是否有其他方法,也许使用未绑定脚本?由于需要一些用户输入(电子表格中的范围等),不可能运行一次单个脚本并让它遍历所有电子表格。
有没有一种方法可以让用户安装一个未绑定的脚本,运行它,然后它会询问要在哪个电子表格上运行它?
openByUrl()
非常接近,但它实际上并没有打开电子表格用户界面,所以我无法使用 getActiveRange()
等功能
如果它是相关的,这里是脚本:
/*function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Normalize')
.addItem('Normalize Crosstab', 'normalizeCrossTab')
.addToUi();
}*/
function onOpen() {
var ss = SpreadsheetApp.getActive();
var items = [
{name: 'Normalize Crosstab', functionName: 'normalizeCrosstab'},
];
ss.addMenu('Normalize', items);
}
/* Converts crosstab format to normalized form. Given columns abcDE, the user puts the cursor somewhere in column D.
The result is a new sheet, NormalizedResult, like this:
a b c Field Value
a1 b1 c1 D D1
a1 b1 c1 E E1
a2 b2 c2 D D2
a2 b2 c2 E E2
...
*/
function normalizeCrosstab() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var firstDataCol = SpreadsheetApp.getActiveRange().getColumn();
var dataCols = values[0].slice(firstDataCol-1);
if (Browser.msgBox("This will create a new sheet, NormalizedResult. Place your cursor is in the first data column.\n\n" +
"These will be your data columns: " + dataCols,Browser.Buttons.OK_CANCEL) == "cancel") {
return;
}
var resultssheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("NormalizedResult");
if (resultssheet != null) {
SpreadsheetApp.getActive().deleteSheet(resultssheet);
}
var header = values[0].slice(0, firstDataCol - 1);
var newRows = [];
header.push("Field");
header.push("Value");
newRows.push(header);
for (var i = 1; i <= numRows - 1; i++) {
var row = values[i];
for (var datacol = 0; datacol < dataCols.length; datacol ++) {
newRow = row.slice(0, firstDataCol - 1); // copy repeating portion of each row
newRow.push(values[0][firstDataCol - 1 + datacol]); // field name
newRow.push(values[i][firstDataCol - 1 + datacol]); // field value
newRows.push(newRow);
}
}
var newSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("NormalizedResult");
var r = newSheet.getRange(1,1,newRows.length, header.length);
r.setValues(newRows);
};
您可以检索用户提供 URL 的 sheet,将它们显示在简单的 HTML 中,一个 sheet 在另一个下面,并附加一个按钮列,这将为该行调用 normalizeCrosstab()。这是已发布的HTML解决方案,任何人无需登录即可使用。
如果有定义数量的 sheet,您也可以在 HTML 中使用名称旁边的按钮生成它们,它会生成 TABLE HTML.
或者使用图书馆,我怀疑你有什么需要但不能做的,那个答案很旧 (12')。
我正在使用库并且没有遇到任何问题,对所有事情都非常方便,所有 sheets 必须具有这 3 个函数才能工作,就好像脚本在 sheet 本身中一样:
function onOpen() {
library.onInitialize();
}
function onEdit(celEd) {
library.onMakeEdit(celEd);
}
function libraryFuncs( funcName, args ){ // Needed for sideBars to use library functions
if(args)
args = args.split("\!|"); // Predefined separation of args
else
args = [];
return library[ funcao ]().apply(this, args);
}
第一个问题是:"Who is the owner of all these sheets?"如果您是所有这些工作表的所有者,那么您有权远程访问它们。如果您不拥有它们,则所有者需要共享并授予试图修改其文件的任何代码的编辑权限。
如果您拥有所有电子表格,则可以创建一个独立应用程序,从一个中心点进行所有处理。然后,您可以将 Stand Alone App 的 link 通过电子邮件发送给每个人,或者让每个用户在他们的电子表格中输入一个 link 到 Stand Alone App。正如您所提到的,对于该选项,您将无法使用 getActiveSheet()
.
无论您使用什么选项,您都需要让人们向他们的电子表格添加内容,或者创建一些新的集中式界面。最适合您的选择可能归结为所有权和设置权限。
我猜如果电子表格的用户是所有者,并且不想授予您许可,他们将需要使用您的前三个选项之一。我会先从图书馆开始。
如果您可以轻松获取电子表格的文件 ID,则可以创建一个将用户与文件 ID 相匹配的对象。
var objUserToFileID = {"user1":"abc34ciu89384u", "user2":"FileID_Two", "user3":"FileID_Three"};
然后让用户从列表中选择他们的名字,(下拉列表)然后 运行 代码。那是针对独立应用程序的。当然,您需要弄清楚如果用户为其他人的电子表格选择文件 ID 会发生什么情况。那么您需要有一种方法来确定应用程序的用户是谁。