【问题标题】:google script detect empty cell after edit or delete谷歌脚本在编辑或删除后检测空单元格
【发布时间】:2018-07-26 22:31:49
【问题描述】:

我的代码有问题。看起来很好,但它不起作用,我真的不知道为什么。我什么都试过了。

我想将我的 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();
}

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

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

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();
  }  

我的测试表屏幕:

【问题讨论】:

    标签: javascript google-apps-script


    【解决方案1】:

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

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

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

      • e.valuee.oldValue 都包含在 e 中。
    3. 在从具有值的单元格中删除值的情况下

      • e.valuee.oldValue 都不包含在 e 中。

    使用上述结果,为了在编辑空单元格和删除单元格值的情况下运行每个函数,您可以使用以下示例脚本。

    示例脚本:

    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.valuee.oldValue 都不包含在 e 中。

    收件人:

    • 在从具有值的单元格中删除值的情况下
      • 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.
    }
    

    注意:

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

    【讨论】:

    • 是的,这就是我想要的。我试过这种方式。顺便说一句,如果你第二个错误,我想你的意思是 e.oldValue!=null 而不是 ==:)。我确实喜欢你的说法,现在程序不会在日历中创建空白事件,但这第二个 if 也不起作用:/
    • @begginer 对于给您带来的不便,我深表歉意。我分别使用("value" in e)!("value" in e)!("oldValue" in e) 作为(e.value != null)(e.value == null)(e.oldValue == null)。在我的环境中,它有效。当它在你的环境中不起作用时,你能提供你当前的脚本吗?我想解决您的问题。
    • 没问题,我们都知道应该怎么做 :)。我添加所有代码。第一个如果工作正常,第二个如果我没有测试并且最后一个不工作:(
    • @I'-'我感谢您的评论。我认为这是一个有趣的观点。在我的环境中,我可以确认e.valuee.oldValue 分别显示"new value""actual oldvalue"。而e.value.oldValueundefined。尽管我通过创建新的电子表格和项目来运行它,但结果是相同的。所以我认为这种差异可能是电子表格版本或规范的变化。您如何看待这种情况?或者如果我对这种情况有误解,请告诉我。
    • @I'-'我感谢您提供更多信息。通过信息,我可以了解情况。我也可以确认。所以我更新了我的答案。你能确认一下吗?非常感谢您的支持。
    【解决方案2】:

    您的情况的问题是您的中间条件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 也将 null 评估为 false,所以就 javascript 而言,null == undefined。为清楚起见,我建议将所有检查更改为 if(!e.value) 或 if(!e.oldValue),或删除对 null 的引用并在任何地方使用 undefined。这将使脚本更具可读性,因为当前的代码看起来像 null 和 undefined 预期会有不同的行为。

    【讨论】:

    • 我试过了,但这个解决方案对我不起作用。我认为当我使用 onEdit 函数时,e.value 是未定义/空的,不像''(当我将 e.value 放入 Logger.log() 时我得到了这个)。我坐了 4 个小时,所以也许我在写愚蠢的东西,但它仍然不起作用:/
    • 对不起,我刚刚测试过,你是对的,当使用 onEdit 触发器时,空单元格不是空白,而是未定义。我将更新我的答案,即使我看到你提到你在你的问题中也尝试过 undefined 。检查与未定义应该可以工作。
    • 我尝试了 null, undefined all :) 当我删除单元格时它仍然在日历中创建空白事件(单元格再次为空白)。一切似乎都很好,但仍然无法正常工作,我不知道问题出在哪里。我也可以将所有代码放在这里,但我认为这不会有帮助。
    • 是的,我建议把其余的代码放在上面,基于 sn-ps 似乎它应该可以工作。
    • 在查看整个代码时,这看起来像是一个小逻辑错误,我已经更新了我的答案
    【解决方案3】:

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

    【讨论】:

    【解决方案4】:

    使用新的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”

    • deleteFromPCB3 使用 Delete 按钮删除。

    • backspaceFromPCB4 被选中并按下 Backspace 按钮。这完全清除了B4

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

    • clearFromMOBILE:在移动应用中,通过长按进入选项并按clear。(B6

    • deleteCharacter1By1MOBILE:同上,但来自移动端。(B7

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

    • overwriteValue7To7.1PC: 同上,但来自B9上的PC。

    结果:

    下面的 json 提供了上面提到的每个action 记录的event objectaction 用作此结果对象的键,而值是实际记录的 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 都不存在。

    【讨论】:

      【解决方案5】:

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

      • 在从具有值的单元格中删除值的情况下:
        • e.value 和 e.oldValue 包含在 e 中。

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


      简短的回答:

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


      情况1

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

      e.oldValue === 未定义

      *注意:如果您没有得到此结果,则可能是应用了不明显的格式,因此请使用“格式”>“清除格式”选项并重试。

      情况2

      如果您对单元格 A1 应用了格式,例如边框、背景颜色或字体(我已经测试了这 3 个,但不是所有其他可能的选项),并且单元格中的文本为“Sample”,并且您使用删除或退格键(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");
        }
      }

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-27
        • 1970-01-01
        相关资源
        最近更新 更多