Google脚本编辑后检测到空单元格

时间:2018-07-26 22:31:49

标签: javascript google-apps-script

我的代码有问题。看起来不错,但是不起作用,我真的不知道为什么。我尝试了一切。

我想将我的google excel与我的google日历同步。一切正常,但现在我只编辑空白单元格时想在日历中创建事件:

if (e.oldValue == null) { // also tried if(e.oldValue == undefinded)
var date = new Date(dayRange);
cal.createAllDayEvent(
  e.value,
  date,
)

}

它工作正常。但是接下来我要在删除单元格时从日历中删除事件(因此它再次为空):

else if(e.value==null){
  var events = cal.getEvents(date, {
  search: ss.getRange(row,2)
  });  
  for (i in events){
    events[i].deleteEvent();
}

在我“删除”日历中的单元格之后,我得到了带有闪烁标题的下一个事件,但我不知道为什么会创建此事件。似乎这种“否则”无效。我真的不知道为什么我阅读了所有示例,代码看起来还可以。

这是我创建触发器的方式:

function createEditTrigger() {
   var ss = SpreadsheetApp.getActive();
   ScriptApp.newTrigger('ToCalendar')
   .forSpreadsheet(ss)
   .onEdit()
   .create();
 }     

我将非常感谢所有建议。

编辑

完整代码:

function ToCalendar(e) {  
    var ss = SpreadsheetApp.getActiveSheet();
    var cal = CalendarApp.getCalendarById("myID");

    var range = e.range;
    var column = range.getColumn();
    var row = range.getRow();

    var day = ss.getRange(1,column);
    var dayRange = day.getValues();

    if ((e.value != null) && (e.oldValue == null)) {
        var date = new Date(dayRange);
         cal.createAllDayEvent(
         ss.getRange(row,2).getValue(),
         date,
         {
             description: e.value,//ss.getRange(row, column).getValue(),
         }
        )
     }

    //If we edit cell:

     else if(e.oldValue!=undefined){
        var events= cal.getEventsForDay(date,{search: e.oldValue});
        var ev= events[0];
        Logger.log(ev);
        ev.deleteEvent();
        cal.createAllDayEvent(ss.getRange(row,2).getValue(),date, 
        {description: ss.getRange(row, column).getValue()})


   // If we delete cell   



    else if((e.value == null) && (e.oldValue != null)){
         var events = cal.getEvents(date, {
         search: ss.getRange(row,2)
       });  
         for (i in events){
            events[i].deleteEvent();
          }

  }

创建触发器:

function createEditTrigger() {
   var ss = SpreadsheetApp.getActive();
   ScriptApp.newTrigger('ToCalendar')
   .forSpreadsheet(ss)
   .onEdit()
   .create();
  }  

我的测试表的屏幕: Sheet

5 个答案:

答案 0 :(得分:2)

您要针对编辑空单元格和删除单元格值的情况运行每个功能。如果我的理解是正确的,该解决方法如何?我认为可能有几种解决方法。因此,请将此视为其中之一。在这种解决方法中,假设您正在使用onEdit(e)。更改单元格后,e中的onEdit(e)如下更改。

  1. 在将值编辑为空单元格的情况下

    • e.value包含在e中。
    • e.oldValue未包含在e中。
  2. 在用一个值覆盖其他值的单元格的情况下

    • e.value中包含e.oldValuee
  3. 从具有值的单元格中删除值

    • e.value不包含e.oldValuee

使用以上结果,为了运行用于编辑空白单元格和删除单元格值的案例的每个功能,可以使用以下示例脚本。

示例脚本:

function onEdit(e) {
  if (("value" in e) && !("oldValue" in e)) {
    Logger.log("In the case for editing a value to empty cell")
  }
  if (!("value" in e) && !("oldValue" in e)) {
    Logger.log("In the case for deleting a value from a cell with a value")
  }
}

当然,您也可以使用以下脚本。

function onEdit(e) {
  if ((e.value != null) && (e.oldValue == null)) {
    Logger.log("In the case for editing a value to empty cell")
  }
  if ((e.value == null) && (e.oldValue == null)) {
    Logger.log("In the case for deleting a value from a cell with a value")
  }
}

注意:

  • 如果您要运行需要授权的方法,请在onEdit()上安装一个触发器。

参考:

如果我误解了你的问题,对不起。

编辑:

已删除语法错误并修改了脚本。在此修改后的脚本中,

  • 编辑空单元格后,将运行if ((e.value != null) && (e.oldValue == null)) { script }中的脚本。
  • 当带有值的单元格被值覆盖时,将运行else if(e.oldValue!=undefined) { script }中的脚本。
  • 删除具有值的单元格的值后,将运行else if((e.value == null) && (e.oldValue == null)) { script }中的脚本。

修改后的脚本:

function ToCalendar(e) {  
  var ss = SpreadsheetApp.getActiveSheet();
  var cal = CalendarApp.getCalendarById("myID");
  var range = e.range;
  var column = range.getColumn();
  var row = range.getRow();
  var day = ss.getRange(1,column);
  var dayRange = day.getValues();
  if ((e.value != null) && (e.oldValue == null)) { // When the empty cell is edited, this becomes true.
    var date = new Date(dayRange);
    cal.createAllDayEvent(ss.getRange(row,2).getValue(),date,{
      description: e.value,//ss.getRange(row, column).getValue(),
    })

  // In your situation, this might not be required.
  } else if(e.oldValue!=undefined) { // When the cell with a value is overwritten by a value, this becomes true.
    //If we edit cell:
    var events= cal.getEventsForDay(date,{search: e.oldValue});
    var ev= events[0];
    Logger.log(ev);
    ev.deleteEvent();
    cal.createAllDayEvent(ss.getRange(row,2).getValue(),date,{description: ss.getRange(row, column).getValue()})

  } else if((e.value == null) && (e.oldValue == null)) { // When the value of cell with a value is removed, this becomes true.
    // If we delete cell
    var events = cal.getEvents(date, {
      search: ss.getRange(row,2)
    });  
    for (i in events){
      events[i].deleteEvent();
    }
  }
}

已添加:

已确认事件对象的规范已更改。确认了以下修改。 @I'-'I提到了这一点。

发件人:

  • 从具有值的单元格中删除值的情况
    • e.value不包含e.oldValuee

收件人:

  • 从具有值的单元格中删除值的情况
    • e.valuee.oldValue包含在e中。
      • e.value{"oldValue":"deleted value"}之类的对象。
      • e.oldValue是类似于"deleted value"的字符串。

通过此修改,当从具有该值的单元格中删除该值时,可以通过以下脚本进行检查。

if (e.value.oldValue) {
    // value was removed from cell.
} else {
    // value is NOT removed from cell.
}

注意:

  • 在将值编辑为空单元格的情况下,以及用一个值用其他值覆盖单元格的情况下,每个结果均不会更改。

答案 1 :(得分:1)

您遇到的问题是您处于中间状况if(e.oldValue!=undefined),这将找出e.value也未定义但e.oldValue未定义的情况,并创建一个事件。因此,您永远都不会遇到希望新擦除的单元删除事件的最终情况。

我认为您想要的逻辑是if(e.oldValue!=undefined && e.value!=undefined)

我认为混淆的部分原因是null和undefined的混合。

从OnEdit触发器接收到时,空单元格的值为undefined,而不是null

由于javascript将undefined评估为false,因此只需检查if(!e.value)(如果没有值)就足够了,但是如果要明确,则可以检查if(e.value === undefined)

因为javascript就JavaScript而言,其null也为false null == undefined。为了清楚起见,我建议将所有检查都更改为if(!e.value)或if(!e.oldValue),或删除对null的引用并在各处使用undefined。这将使脚本更具可读性,因为当前代码使null和undefined看起来表现出不同的行为。

答案 2 :(得分:0)

从2019年10月21日起,我不再发现这些变通办法可以正常工作。当一个单元格有一个值,然后用退格键删除该值或删除击键时,google会按预期返回e.oldvalue,但是e.value中有一个奇数对象,而不是虚假的响应。这是一个非常令人困惑的错误,对于那些尝试使用gSheets进行自动化的人来说确实令人困惑。现在,新的解决方法包括检查数据类型。这是link to the new issue的完整文档,包括新的解决方法。

答案 3 :(得分:0)

使用新的V8引擎的最新结果(2020)建议,在删除单元格或后退空格或逐个删除每个字符时,始终包含e.oldValue

动作:

以下动作被一个接一个地执行,并且事件对象被记录在每个动作上,

const onEdit = e => console.log(JSON.stringify(e));
|   | B(Value)      | Action taken               |
|---+---------------+----------------------------|
| 3 | deletedValue1 | deleteFromPC/insertValue   |
| 4 | deletedValue2 | backspaceFromPC            |
| 5 | deletedValue3 | deleteCharacter1by1PC      |
| 6 | deletedValue4 | clearFromMOBILE            |
| 7 | deletedValue5 | deleteCharacter1By1MOBILE  |
| 8 | deletedValue6 | overwriteValue6To6.1MOBILE |
| 9 | deletedValue7 | overwriteValue7To7.1PC     |
  • insertValue:将文本“ deletedValue1”插入B3中。

  • autofillInsertedValueB3中的文本自动填充到B9B3:B9包含“ deletedValue1”到“ deletedValue7”

  • deleteFromPC:使用 Delete 按钮删除B3

  • backspaceFromPC:已选择B4,并按下了 Backspace 按钮。这会完全清除B4

  • deleteCharacter1by1PC:选择B5>按下 Enter 进入编辑模式>每个字符被一个个清除> Enter 退出编辑模式。

  • clearFromMOBILE:在移动应用中,长按输入选项,然后按下 clear 。(在B6上)

  • deleteCharacter1By1MOBILE:与上面相同,但来自Mobile。(在B7上)

  • overwriteValue6To6.1MOBILEB8的值从“ deletedValue6”覆盖为“ deletedValue6.1”

  • overwriteValue7To7.1PC:与上述相同,但从PC上的B9

结果:

以下json提供了上述每个event object记录的actionaction用作此结果对象的键,而值是实际记录的event object

{
  "insertValue": {
    "authMode": "LIMITED",
    "range": { "columnEnd": 2, "columnStart": 2, "rowEnd": 3, "rowStart": 3 },
    "source": {},
    "user": { "email": "", "nickname": "" },
    "value": "deletedValue1"
  },
  "autofillInsertedValue": {
    "authMode": "LIMITED",
    "range": { "columnEnd": 2, "columnStart": 2, "rowEnd": 9, "rowStart": 4 },
    "source": {},
    "user": { "email": "", "nickname": "" }
  },
  "deleteFromPC": {
    "authMode": "LIMITED",
    "oldValue": "deletedValue1",
    "range": { "columnEnd": 2, "columnStart": 2, "rowEnd": 3, "rowStart": 3 },
    "source": {},
    "user": { "email": "", "nickname": "" }
  },
  "backspaceFromPC": {
    "authMode": "LIMITED",
    "oldValue": "deletedValue2",
    "range": { "columnEnd": 2, "columnStart": 2, "rowEnd": 4, "rowStart": 4 },
    "source": {},
    "user": { "email": "", "nickname": "" }
  },
  "deleteCharacter1by1PC": {
    "authMode": "LIMITED",
    "oldValue": "deletedValue3",
    "range": { "columnEnd": 2, "columnStart": 2, "rowEnd": 5, "rowStart": 5 },
    "source": {},
    "user": { "email": "", "nickname": "" }
  },
  "clearFromMOBILE": {
    "authMode": "LIMITED",
    "oldValue": "deletedValue4",
    "range": { "columnEnd": 2, "columnStart": 2, "rowEnd": 6, "rowStart": 6 },
    "source": {},
    "user": { "email": "", "nickname": "" }
  },
  "deleteCharacter1By1MOBILE": {
    "authMode": "LIMITED",
    "oldValue": "deletedValue5",
    "range": { "columnEnd": 2, "columnStart": 2, "rowEnd": 7, "rowStart": 7 },
    "source": {},
    "user": { "email": "", "nickname": "" }
  },
  "overwriteValue6To6.1MOBILE": {
    "authMode": "LIMITED",
    "oldValue": "deletedValue6",
    "range": { "columnEnd": 2, "columnStart": 2, "rowEnd": 8, "rowStart": 8 },
    "source": {},
    "user": { "email": "", "nickname": "" },
    "value": "deletedValue6.1"
  },
  "overwriteValue7To7.1PC": {
    "authMode": "LIMITED",
    "oldValue": "deletedValue7",
    "range": { "columnEnd": 2, "columnStart": 2, "rowEnd": 9, "rowStart": 9 },
    "source": {},
    "user": { "email": "", "nickname": "" },
    "value": "deletedValue7.1"
  }
}

推论:

  • e.oldValue键:

      当单元格被删除(以任何方式)或被覆盖时,
    • 存在于所有事件中
    • 不存在,当用新值编辑或自动填充空单元格时。
  • e.value键:

      当插入新值或覆盖旧值时,出现
    • 不存在,当删除值或自动填充单元格时。
  • 如果单元格是自动填充的,则valueoldValue都不存在。

答案 4 :(得分:0)

对于@Tanaike 提到的使用 Google 表格上的删除或退格键删除单元格时无法让 e.oldValue 返回旧值的任何人:

<块引用>
  • 对于从具有值的单元格中删除值的情况:
    • e.value 和 e.oldValue 包含在 e 中。

我无法让 e.oldValue 返回“未定义”以外的任何内容,我在这里找不到答案,但 @Tanaike 解释了 solution in more detail here


简答:

e.oldValue 的结果取决于您是否对编辑的单元格应用了格式(单元格边框、背景阴影等)。


情况 1

如果您将默认格式应用于单元格 A1 并在单元格中使用文本“示例”,并且您使用删除或退格键(Mac 或 PC)从单元格中删除文本,您将获得以下结果:

e.oldValue === 未定义

*注意:如果你没有得到这个结果,你可能应用了不明显的格式,所以使用格式>清除格式选项并重试。

情况 2

如果您将格式应用于单元格 A1,例如边框、背景颜色或字体(我已经测试了这 3 个,但不是所有其他可能的选项),并且单元格中的文本为“示例”,并且您使用删除或退格键(Mac 或 PC)从单元格中删除文本,您将得到以下结果:

e.oldValue === 示例

因此,通过以某种方式设置单元格、列或整个工作表的格式,您可以在删除之前返回并根据已编辑单元格的值进行测试。

可以使用以下代码在空白的 Google Sheet 上测试此行为:

function onEdit(e) {
  console.log("This is the current value in e: " + e.value);
  console.log("This is the Old Value in e: " + e.oldValue);
  if (e.value) {
    console.log("There is a value in e");
  }
  if (e.oldValue) {
    console.log("There is an old value in e");
  }
  if (!e.value) {
    console.log("There is NOT a value in e");
  }
  if (!e.oldValue) {
    console.log("There is NOT an old value in e");
  }
}