Обнаружить вставка строке или столбце в таблицы Google и реагировать в скрипте

в Google Apps Script одним из основных инструментов является триггер onEdit в электронной таблице, который позволяет нам определять, когда пользователь редактирует ячейку, и реагировать на нее.

Как насчет того, когда пользователь вставляет строку или столбец ? Есть ли способ обнаружить это ?

это вызовет onEdit ? Если это так, я думаю, поддержание в ScriptDb количества строк или столбцов, а затем проверка каждый раз будет делать, но будет очень дорогостоящим, так как getMaxRows () уже довольно медленно, и обращение к ScriptDb также.

Что вы думаете ?

5 ответов


есть ряд действий редактирования, которые не вызывают onEdit(), это не полный список, есть еще много сообщений об исключениях:

если вы хотите знать, сколько строк в электронной таблице, это занимает около 120 мс для выполнения:

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

Я уже показал, что быстрее записать значение на лист чем использовать ScriptDB. Вы можете ожидать незначительное время, чтобы написать небольшой диапазон, около 1ms.

Итак, если вы может обнаружение строки или столбца добавляется, это будет стоить вам менее 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.)");
  }
}

Google добавил событие "On Change", которое обнаруживает вставку/удаление строк/столбцов вместе с другими типами изменений, типы для которых вы можете видеть здесь под допустимыми значениями для changeType. Ниже приведены инструкции здесь подробно о том, как добавить триггер в проект, чтобы вы могли вызвать свою функцию при возникновении события "On Change".

чтобы вручную создать устанавливаемый триггер через диалоговое окно в скрипте редактор, выполните следующие действия:

  1. в Редакторе сценариев выберите Edit > триггеры текущего проекта.
  2. нажмите на ссылку, которая говорит: триггеры не настроены. Нажмите здесь, чтобы добавить один сейчас.
  3. под выполнить выберите имя функции, которую нужно вызвать.
  4. под событий выберите времени или приложение Google скрипт привязан to (например,из таблицы).
  5. выберите и настройте тип триггера, который вы хотите создать (например,часовой таймер, который работает каждый час или на open триггер).
  6. дополнительно нажмите кнопку уведомления чтобы настроить, как и когда с вами свяжутся по электронной почте, если ваша срабатывающая функция не сработает.
  7. клик сохранить.

на шаге 4 Вы бы выбрали из таблицы и на шаге 5 вы бы выбрали на Изменить. Это должно произвести желаемый эффект. Есть также опции для добавления триггеров программно и запроса авторизации, если вы пытаетесь использовать это в надстройке для распространения среди пользователей. Оба подробно описаны в Установить Триггеры документация.


есть еще один способ, который я недавно использовал. Каждый раз, когда onEdit() вызывается, она возвращает событие объекта (e) это дает вам некоторую ценную информацию о том, что происходит.

например, он дает вам диапазон, который вы можете получить из e.диапазон. Оттуда вы можете поперечно по-разному и узнать, например, какая строка редактируется. Но в объекте e есть и более полезные данные. Это дает вам " oldvalue" (Эл.старого значения) ячейки, которую вы редактировали, и новое значение (Эл.значение).

одним из возможных способов смешать всю эту информацию вместе было бы получить диапазон, соответствующий строке, которую вы редактируете, а затем проверить, пусты ли все ячейки (но тот, который вы только что отредактировали), и если нет 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;
}

У меня были проблемы с этим, пока я не дал разрешения на скрипт. В противном случае функциональность 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"));
}

Я играл с 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.');
}