使用GAS为Google电子表格编写"撤消"功能

Chr*_*ice 5 google-sheets google-apps-script

目前undo(),电子表格/表格/范围类中没有Google Apps脚本功能.在问题跟踪器上打开了一些问题,我现在只能找到一个(我不知道Triaged的含义):这里.

有人提出使用DriveApp和修订历史记录的变通方法,但我环顾四周并没有找到任何东西(也许它被埋没了?).无论如何,undo()对于许多不同的操作来说,功能是非常必要的.我只能想到一种解决方法,但我无法让它工作(数据的存储方式,我不知道它是否可能).这是一些伪 -

function onOpen () {
  // Get all values in the sheet(s)
  // Stringify this/each (matrix) using JSON.stringify
  // Store this/each stringified value as a Script or User property (character limits, ignore for now)
}

function onEdit () {
  // Get value of edited cell
  // Compare to some value (restriction, desired value, etc.)
  // If value is not what you want/expected, then:
  // -----> get the stringified value and parse it back into an object (matrix)
  // -----> get the old data of the current cell location (column, row)
  // -----> replace current cell value with the old data
  // -----> notifications, coloring cell, etc, whatever else you want
  // If the value IS what you expected, then:
  // -----> update the 'undoData' by getting all values and re-stringifying them
  //        and storing them as a new Script/User property
}
Run Code Online (Sandbox Code Playgroud)

基本上,当打开电子表格时,将所有值存储为脚本/用户属性,并仅在满足某些单元格条件(on)时引用它们.如果要撤消,请获取存储在当前单元位置的旧数据,并将当前单元格的值替换为旧数据.如果不需要撤消该值,则更新存储的数据以反映对电子表格所做的更改.

到目前为止,我的代码已经破了,我认为这是因为嵌套数组结构在对象被字符串化和存储时丢失(例如,它没有正确解析).如果有人写过这种功能,请分享.否则,有关如何编写此内容的建议将会有所帮助.

编辑:这些文件非常静态.行/列的数量不会改变,数据的位置也不会改变.如果可能的话,为临时修订历史实现get-all-data/store-all-data -type函数实际上将满足我的需求.

小智 9

当我需要保护工作表但允许通过侧边栏进行编辑时,我遇到了类似的问题.我的解决方案是有两张纸(一张隐藏).如果编辑第一个工作表,则会触发onEdit过程并从第二个工作表重新加载值.如果您取消隐藏并编辑第二张纸,则会从第一张纸重新加载.工作完美,非常有趣,删除大众数据,并观看它自我修复!


Mog*_*dad 5

只要不添加或删除行和列,就可以依靠行号和列号作为存储在 ScriptDb 中的历史值的索引。

function onEdit(e) {
  // Exit if outside validation range
  // Column 3 (C) for this example
  var row = e.range.getRow();
  var col = e.range.getColumn();
  if (col !== 3) return;
  if (row <= 1) return; // skip headers

  var db = ScriptDb.getMyDb();

  // Query database for history on this cell
  var dbResult = db.query({type:"undoHistory",
                       row:row,
                       col:col});
  if (dbResult.getSize() > 0) {
    // Found historic value
    var historicObject = dbResult.next();
  }
  else {
    // First change for this cell; seed historic value
    historicObject = db.save({type:"undoHistory",
                              row:row,
                              col:col,
                              value:''});
  }

  // Validate the change.
  if (valueValid(e.value,row,col)) {
    // update script db with this value
    historicObject.value = e.value;
    db.save(historicObject);
  }
  else {
    // undo the change.
    e.range.getSheet()
           .getRange(row,col)
           .setValue(historicObject.value);
  }
}
Run Code Online (Sandbox Code Playgroud)

您需要提供一个函数来验证您的数据值。同样,在此示例中,我们只关心一列中的数据,因此验证非常简单。例如,如果您需要对不同的列执行不同类型的验证,那么您可以switchcol参数进行验证。

/**
 * Test validity of edited value. Return true if it
 * checks out, false if it doesn't.
 */
function valueValid( value, row, col ) {
  var valid = false;

  // Simple validation rule: must be a number between 1 and 5.
  if (value >= 1 && value <= 5)
    valid = true;

  return valid;
}
Run Code Online (Sandbox Code Playgroud)

合作

此撤消功能适用于协作编辑的电子表格,尽管在脚本数据库中存储历史值存在竞争条件。如果多个用户同时对一个单元格进行了第一次编辑,则数据库最终可能会出现多个代表该单元格的对象。在后续更改中,query()仅选择第一个结果的使用和选择可确保仅选择这些倍数中的一个。

如果这成为一个问题,可以通过将函数封装在锁中来解决。

  • ScriptDB [不再工作了吗?](https://developers.google.com/apps-script/sunset) (2认同)