检测用户插入行或列中一个谷歌的电子表格和在脚本中进行反应(Detect user inserting

2019-07-21 01:31发布

在谷歌Apps脚本,基本工具之一是在电子表格中,这使我们当用户编辑一个细胞进行检测,并做出反应它的onEdit触发。

如何在用户插入的行或列? 有没有一种方法来检测?

会触发一个onEdit? 如果是这样,我在一个ScriptDb猜维护的行或列的数量的计数,然后每次会做检查,但会是很长的时间成本,因为getMaxRows()已经相当缓慢,并深入到一个ScriptDb是好。

你怎么看 ?

Answer 1:

有一些不触发编辑操作的onEdit()这是不是一个完整列表,有更多的报道排除 :

  • 表单提交发出4568 。
  • 插入或删除行或列问题1363
  • 在外观上的变化(字体,格式,对齐方式,边框) 发行2476
  • 填写范围(例如用1,2,3开始,选择这些细胞,并拖动“手柄”,以填补更多的细胞与4,5,6 ...) 发行399
  • 写入值的脚本。 338期
  • 查找/替换问题1754
  • 问题1568 , 问题1119

如果你想知道有多少行是在电子表格中,这大约需要120毫秒执行:

var numCols = SpreadsheetApp.getActiveSheet().getRange("1:1").getLastColumn();
var numRows = SpreadsheetApp.getActiveSheet().getRange("A:A").getLastRow();

我已经表明, 它的速度更快的值写入到一个表 ,而不是使用一个ScriptDb。 你可以期待一个微不足道的时间来写一个很小的范围内,围绕为1ms。

所以,如果你检测到添加的行或列,它会花费你不到2个0.2秒到办理变更登记。 此onEdit()演示了一种技术来测量电子表格的程度,并报告在片尺寸的变化。 (为了测试,添加或删除行或列,然后进行触发编辑onEdit()它还包含定时器-随时与测量和/或存储值,看看有什么最适合你的其他方式进行试验。

function onEdit() {
  // Use start & stop to time operations
  var start = new Date().getTime();

  // We want the size of the sheet, so will select ranges across and down the
  // whole sheet. Cannot use getDataRange(), as it selects only occupied cells.
  var numCols = SpreadsheetApp.getActiveSheet().getRange("1:1").getLastColumn()
  var numRows = SpreadsheetApp.getActiveSheet().getRange("A:A").getLastRow();

  var stop = new Date().getTime();
  var timeToMeasure = (stop-start);

  // Did things change?
  var oldSize = SpreadsheetApp.getActiveSheet().getRange("A1:B1").getValues();
  if (oldSize[0][0] != numCols || oldSize[0][1] != numRows) {
    // Yes, they did - Let's store the new dimensions
    start = new Date().getTime();

    SpreadsheetApp.getActiveSheet().getRange("A1:B1").setValues([[numCols,numRows]]);

    var stop = new Date().getTime();
    var timeToStore = (stop-start);  

    Browser.msgBox("Sheet is "+numCols+" by "+numRows+"."
                  +" ("+timeToMeasure+"ms to measure, "+timeToStore+"ms to store.)");
  }
}


Answer 2:

谷歌增加了“关于变更”事件检测与其他类型的变化,为此,你可以看到类型沿行/列插入/缺失这里下的允许值changeType 。 以下是从说明这里详细介绍了如何触发添加到您的项目,这样你可以有你的功能,当“关于变更”事件发生时调用。

手动创建通过脚本编辑器对话框可安装触发,请按照下列步骤操作:

  1. 从脚本编辑器,选择编辑>当前项目的触发器
  2. 点击上面写着链接: 没有触发器设置。 点击这里现在添加一个
  3. 运行中,选择要触发的功能名称。
  4. 活动中,选择时间驱动或者谷歌应用程序,该脚本绑定到(例如, 从电子表格 )。
  5. 选择和配置您要创建(例如,运行的每个小时在打开触发一个小时定时器 )触发器的类型。
  6. 另外,单击通知配置如何,当你将通过电子邮件,如果你的触发功能无法联络。
  7. 单击保存

在步骤4,你会从电子表格中选择,并在步骤5中,你会选择在变化 。 这应该有你要寻找的效果。 也有以编程方式添加触发器和请求授权,如果你想使用这个在附加分发给用户的选项。 两者都是在详细安装的触发器文档。



Answer 3:

还有就是我刚刚最近使用的另一种方式。 每次onEdit()被触发时,它会返回一个事件对象 (E),让您了解正在发生的事情一些有价值的信息。

例如,它给你的范围,你可以从e.range检索。 从那里,你可以在许多不同的方式横向和了解,例如,该行正在编辑。 但是,还有更多有用的数据,以及在电子对象。 它给你你编辑的单元格的“OLDVALUE”(e.oldValue),而新的值(e.value)。

所有这些信息混合在一起的一种可能的方式是获得与您正在编辑的行的范围,然后检查人的细胞是空的(但一个刚才编辑),如果没有属性oldValue。

这并不一定符合您的电子表格的最后一排,但一个空行。 如果你与你如何填写您的数据是一致的,这可能为你工作:

//val = inserted value (e.value);
//old = old Value (e.oldValue);
//col = number of column being edited
//arr = array with the indexes of the columns that should be completed so as to make a new row [0,1,2...n]
function isInsert(old, val, col, arr){
   if((typeof val != "object")&&!old&&(arr.some(isNotEmpty, col)))
     return true;
   else
     return false;
}

function isNotEmpty(el){
   if(this == el)
     return true;
}


Answer 4:

我是有这个麻烦,直到我把权限给脚本。 否则PropertiesService功能将无法正常工作。 一旦我做到了,我能检测哪些行已插入下面的代码:

var props = PropertiesService.getUserProperties();

function onEdit(e) {
  props.setProperty("firstRow", e.range.getRow());
  props.setProperty("lastRow", e.range.getLastRow());
}


function onChange(e){
if(e.changeType=="INSERT_ROW")
    SpreadsheetApp.getUi().alert("Inserted Rows: " +
                                 props.getProperty("firstRow") + 
                                 " - " +
                                 props.getProperty("lastRow"));
}


Answer 5:

我一直在玩弄onEdit和的onChange。 该onEdit响应,可以访问被编辑的行。 不幸的是,响应的onChange不允许你这样做。 因此,对于一个强大的解决方案,它看起来像你需要呼吁双方触发器。 如果您的工作表不需要空行/列,下面的脚本删除任何新添加的行/列,删除所有空行/列(在情况下,用户批量添加的行/列),然后警告说,他们不能添加行或用户列:

//////////////////////
// Global Variables //
//////////////////////

var SHEET = SpreadsheetApp.getActiveSheet();
var PROPERTIES = PropertiesService.getScriptProperties();

////////////////////
// Event Triggers //
////////////////////

/**
 * Track original sheet row/column count and register onChange trigger.
 */
function onOpen()
{
    // Set original dimensions
    PROPERTIES.setProperty('rows', SHEET.getMaxRows());
    PROPERTIES.setProperty('columns', SHEET.getMaxColumns());

    // Create onChange trigger
    ScriptApp
        .newTrigger('deleteNewRowsAndColumns')
        .forSpreadsheet(SpreadsheetApp.getActive())
        .onChange()
        .create();
}

/**
 * If new rows or columns were added to the sheet
 * warn the user that they cannot perform these
 * actions and delete empty (new) rows and columns.
 *
 * @param e
 */
function deleteNewRowsAndColumns(e)
{
    switch(e.changeType) {
        case 'INSERT_COLUMN':
            removeEmptyColumns();
            warn();
            break;
        case 'INSERT_ROW':
            removeEmptyRows();
            warn();
            break;
        default:
            return
    }
}

///////////////
// Utilities //
///////////////

/**
 * Remove empty columns.
 *
 * This function assumes you have a header row in which
 * all columns should have a value. Change headerRow value
 * if your headers are not in row 1.
 */
function removeEmptyColumns() {
    var maxColumns = SHEET.getMaxColumns();
    var lastColumn = SHEET.getLastColumn();
    if (maxColumns - lastColumn != 0) {
        // New column(s) were added to the end of the sheet.
        SHEET.deleteColumns(lastColumn + 1, maxColumns - lastColumn);
    } else {
        // New column was added in the middle of the sheet.
        // Start from last column and work backwards, delete
        // first column found with empty header cell.
        var headerRow = 1;
        var headers =  SHEET.getRange(headerRow, 1, 1, lastColumn).getValues()[0];
        for (var col = lastColumn; col >= 1; col--) {
            if (headers[col -1] == '') {
                SHEET.deleteColumn(col);
                // Since can only insert one column to the left
                // or right at a time, can safely exit here;
                break;
            }
        }
    }
}

/**
 * Remove empty rows.
 *
 * This function assumes that all rows should
 * have data in the first cell.
 */
function removeEmptyRows() {
    var maxRows = SHEET.getMaxRows();
    var lastRow = SHEET.getLastRow();
    if (maxRows-lastRow != 0) {
        // New row(s) were added to the end of the sheet.
        SHEET.deleteRows(lastRow + 1, maxRows - lastRow);
    } else {
        // New row was added in the middle of the sheet.
        // Start from last column and work backwards, delete
        // first empty column found.
        var values = SHEET.getRange('A:A').getValues();
        var startIndex = values.length - 1;
        for (var i = startIndex; i >= 0; i--) {
            if (values[i] && values[i][0] == '') {
                SHEET.deleteRow(i + 1);
                // User can bulk add rows to the bottom of the file
                // but can only add 1 above or below at a time in the
                // middle of the file, so it's safe to exit here.
                break;
            }
        }
    }
}

/**
 * Return user warning message about adding new rows and columns
 */
function warn()
{
    SpreadsheetApp.getUi().alert('You cannot add new rows or columns.');
}


文章来源: Detect user inserting row or column in a google spreadsheet and reacting in a script