google-sheetsgoogle-apps-scriptundo

I am unable to undo more than one step back when using a particular Apps Script


Beginning of... I added this after posting the question below

For example, entering "ulianababenko" into either A2 or B2 would cause both A2 and B2 to display "ulianababenko". Yes. A2 and B2 would display the same text.

However, A2 would link to behance.net/ulianababenko whereas B2 would link to behance.net/ulianababenko/moodboards. In other words, A2 would link to the first URL; and B2 would link to the second URL.

End of... I added this after posting the question below

Normally in an ordinary Google Sheet, when I press Ctrl + z repeatedly, I am able to undo many steps back.

However, in this Google Sheet --> Behance.net- sample spreadsheet, when I change, for example, A3 from test1 to Ben and then change A4 from test2 to Jacob, and then press Ctrl + z once I am able to undo Jacob (in A4). However, when I press Ctrl + z a second time, I am unable to undo Ben (in A3).

In such a case, of course, I would like to be able to undo Ben (in A3).

The Google sheet linked to above contains the following Apps Script....

function onEdit(e){
var sh = e.source.getActiveSheet()
for (var i = e.range.rowStart;i<=e.range.rowEnd;i++){
    for (var j = e.range.columnStart;j<=e.range.columnEnd;j++){
    myFunction(sh,sh.getRange(i,j),sh.getRange(i,j).getValue())
    }
}
}

function myFunction(sheet,range,value){
if (sheet.getName() == "RemcoE33" && range.getColumn() < 3 && range.getRow() > 1) {
    const urls = [`https://www.behance.net/${value}`,`https://www.behance.net/${value}/moodboards`];
    const richTextValues = [];
    console.log(urls);
    urls.forEach(url => {
    richTextValues.push(SpreadsheetApp.newRichTextValue()
        .setText(value)
        .setLinkUrl(url)
        .build())
    });
    sheet.getRange(range.getRow(), 1, 1, 2).setRichTextValues([richTextValues]);
}
}

I tried the following but it caused the original scrip to stop working properly

function onEdit(e){
  var sh = e.source.getActiveSheet()
  for (var i = e.range.rowStart;i<=e.range.rowEnd;i++){
    for (var j = e.range.columnStart;j<=e.range.columnEnd;j++){
      myFunction(sh,sh.getRange(i,j),sh.getRange(i,j).getValue())
    }
  }
}


function myFunction(sheet,range,value){
if (sheet.getName() == "RemcoE33" && range.getColumn() < 3 && range.getRow() > 1) {
    const urls = [`https://www.behance.net/${value}`,`https://www.behance.net/${value}/moodboards`];
    const richTextValues = [];
    console.log(urls);
    urls.forEach(url => {
    richTextValues.push(SpreadsheetApp.newRichTextValue()
        .setText(value)
        .setLinkUrl(url)
        .build())
    });
    sheet.getRange(range.getRow(), 1, 1, 2).setRichTextValues([richTextValues]);
}
}


function onEdit(e){
  if (!e.oldValue.includes("https://www.behance.net")) {
    var sh = e.source.getActiveSheet()
    for (var i = e.range.rowStart;i<=e.range.rowEnd;i++){
      for (var j = e.range.columnStart;j<=e.range.columnEnd;j++){
        myFunction(sh,sh.getRange(i,j),sh.getRange(i,j).getValue())
      }
    }
  }
}

I tried the following on 29 January 2022 but it caused the original scrip to stop working properly

function onEdit(e){
  if (!e.oldValue.includes("https://www.behance.net")) {
    var sh = e.source.getActiveSheet()
    for (var i = e.range.rowStart;i<=e.range.rowEnd;i++){
      for (var j = e.range.columnStart;j<=e.range.columnEnd;j++){
        myFunction(sh,sh.getRange(i,j),sh.getRange(i,j).getValue())
      }
    }
  }
}

function myFunction(sheet,range,value){
if (sheet.getName() == "RemcoE33" && range.getColumn() < 3 && range.getRow() > 1) {
    const urls = [`https://www.behance.net/${value}`,`https://www.behance.net/${value}/moodboards`];
    const richTextValues = [];
    console.log(urls);
    urls.forEach(url => {
    richTextValues.push(SpreadsheetApp.newRichTextValue()
        .setText(value)
        .setLinkUrl(url)
        .build())
    });
    sheet.getRange(range.getRow(), 1, 1, 2).setRichTextValues([richTextValues]);
}
}

Solution

  • Issue:

    An onEdit function that changes the value that was just edited doesn't work well with undoing changes.

    Explanation:

    For every edit your users make, there's an additional edit made by your script: turning the new values to rich text values.

    Therefore, in order to undo each manual edit, you should undo the action twice (one for the manual edit and one for the script edit). But, each time you undo an action via ctrl + z, that "undo" counts as a further edit and your script gets triggered again, making in its turn an additional change to the spreadsheet.

    That is, every time you try to undo a change, the script makes an additional change. So, if you want to undo multiple actions, you'll have to press ctrl + z many times and fast enough so as to not give the script time to change the value again.

    Workaround:

    As a workaround for your specific case, I'd suggest checking whether the old value (pre-edit) contains the string https://www.behance.net (using the event object property e.oldValue). If I understand your workflow correctly, that will mean the script is triggering due to an "undo" action, and so it should not make an additional edit.

    function onEdit(e){
      try {
        if (!e.oldValue.includes("https://www.behance.net")) {
          update(e);
        }
      } catch(err) {
        update(e);
      }
    
    }
    
    function update(e) {
      var sh = e.source.getActiveSheet()
      for (var i = e.range.rowStart;i<=e.range.rowEnd;i++){
        for (var j = e.range.columnStart;j<=e.range.columnEnd;j++){
          myFunction(sh,sh.getRange(i,j),sh.getRange(i,j).getValue())
        }
      }
    }
    
    function myFunction(sheet,range,value){
    if (sheet.getName() == "RemcoE33" && range.getColumn() < 3 && range.getRow() > 1) {
        const urls = [`https://www.behance.net/${value}`,`https://www.behance.net/${value}/moodboards`];
        const richTextValues = [];
        console.log(urls);
        urls.forEach(url => {
        richTextValues.push(SpreadsheetApp.newRichTextValue()
            .setText(value)
            .setLinkUrl(url)
            .build())
        });
        sheet.getRange(range.getRow(), 1, 1, 2).setRichTextValues([richTextValues]);
    }
    }