繁体   English   中英

Google Apps Scripts:从 .showModalDialog 表单提交运行两次的函数

[英]Google Apps Scripts: Function running twice from .showModalDialog form submit

这是我的第一篇文章,我已经搜索了几天,但找不到解决此问题的方法。

我在工作表中有一个自定义菜单,可以弹出 .showModalDialog html。 用户填写信息并点击提交。 这会在后端运行一个创建文件夹/文件并将用户数据添加到各种工作表等的功能。

所有这些都在工作 我有一个 Ui.alert,我用它来检查输入的数据是否正确,由于某种原因,该功能被触发两次,结果 UI.alert 再次弹出。 我有一个故障保险柜,可以检查其中一个字段是否存在,因此它不会再次写入,但弹出窗口是一种非常糟糕的用户体验。

任何帮助将非常感激。 创建自定义菜单的功能:

function onOpen() { 
  SpreadsheetApp.getUi() 
      .createMenu('TOA - Menu')
      .addItem('Add New Account', 'addAccount')
      .addItem('Update Brand', 'updateBrand')
      .addItem('Go Live', 'goLive')
      .addToUi();
}

调出表格的功能:

function addAccount() {
  const html = HtmlService.createHtmlOutputFromFile('newAccount')
      .setTitle('Add New Account')
      .setWidth(1000)
      .setHeight(800);;
  SpreadsheetApp.getUi() 
      .showModalDialog(html, 'Add New Account');
}

表格代码:

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <style>
      #itemDisplay {
        display: none;
      }
      #modDisplay {
        display: none;
      }
      #priceDisplay {
        display: none;
      }
      #businessTypeDisplay {
        display: none;
      }
    </style>

  </head>
  <body>
    <h1>
        Add New Account
    </h1>

  <form id="myForm" onsubmit="handleNewAccountFormSubmit(this);"> 
    <div>
    <label for="newBrand">Brand:</label>
    <input name="newBrand" type="text" placeholder="Brand Name" required>
    </div>

    <div>
    <p>Location</p>
    <label for="country">Country:</label>
    <select name="country" id="" onchange="" required>
      <option value="" disabled selected>Select Country</option> 
      <option value="US">US</option>     
    </select>

    <label for="state">State/Province:</label>
    <input name="state" type="text" placeholder="State or province" required>

    <label for="city">City:</label>
    <input name="city" type="text" placeholder="City" required>


    <div>
    <label for="businessType">Business Type:</label>
    <select name="businessType" id="businessTypeSelect" onchange="businessOtherDisplay(this.value);" required>
      <option value="" disabled selected>Select Request Reason</option> 
      <option value="americanDiner">American Diner</option>
      <option value="pizzaParlor">Pizza Parlor</option>
      <option value="coffeeShop">Coffee Shop</option>
      <option value="candyShop">Candy Store</option>
      <option value="iceCreamParlor">Ice Cream Parlor</option>
      <option value="burgerShop">Burger Shop</option>
      <option value="otherNon_AmericanDiner">Other non-American Diner (Foreign servings)</option>
      <option value="other">Other (not listed above)</option>
    </select>
    </div>

    <div id="businessTypeDisplay">
    <label for="businessTypeOther">Business Type Other:</label>
    <input name="businessTypeOther" type="text" placeholder="Business type if not listed above">
    </div>

    <div>
    <label for="integration">Integration:</label>
    <select name="integration" required>
      <option value="" disabled selected>Select Request Reason</option> 
      <option value="square">Square</option>
      <option value="clover">Clover</option>
      <option value="cloverPilot">Clover Pilot</option>
      <option value="stripe">Stripe</option>
      <option value="gPay">GPAY</option>
      <option value="others" >Others</option>
    </select>
    </div>

    <label for="menuSource">File Attachment/Source:</label>
    <input name="menuSource" type="text" placeholder="Path to menu" required url>

    <div>
    <p>Do you need an item hidden/disabled?</p>
    <label for="yes">Yes</label>
    <input name="disableItemOption" type="radio" value="yes" onclick="showItem()">
    <label for="no">No</label>
    <input name="disableItemOption" type="radio" value="no" checked onclick="hideItem()">
    </div>

    <div id="itemDisplay">
    <label for="itemDisable">Which item(s) should be disabled?</label>
    <textarea id="disabledItem" name="itemDisable" cols="40" rows="5"></textarea>
    </div>

    <div>
    <p>Do you need a modifier hidden/disabled?</p>
    <label for="yes">Yes</label>
    <input name="disableModOption" type="radio" value="yes" onclick="showMod()">
    <label for="no">No</label>
    <input name="disableModOption" type="radio" value="no" checked onclick="hideMod()">
    </div> 

    <div id="modDisplay">
    <label for="modDisable">Which modifier(s) should be disbaled?</label> 
    <textarea id="disabledMod" name="modDisable" cols="40" rows="5"></textarea>
    </div>   

    <div>
    <p>Do you need to update a price?</p>
    <label for="yes">Yes</label>
    <input name="updatePrice" type="radio" value="yes" onclick="showPrice()">
    <label for="no">No</label>
    <input name="updatePrice" type="radio" value="no" checked onclick="hidePrice()">
    </div>

    <div id="priceDisplay">
    <label for="priceUpdate">Which item/modifier needs a price update?</label>
    <textarea id="updatedPrice" name="priceUpdate" cols="40" rows="5" priceUpdate></textarea>
    </div>  

    <div>
    <label for="otherUpdates">Any other information needed on the menu?</label>  
    <input name="otherUpdates" type="text" placeholder="List other instructions here">
    </div>

    <div>
    <label for="specialInstructions">Are there special instructions/notes for this brand?</label>  
    <input name="specialInstructions" type="text" placeholder="List special instructions here">
    </div>

    <input id="submitButton" type="submit" value="Submit">
    <input type="button" value="Cancel" onclick="google.script.host.close()">
    </div>
  </form>  
  <script>

      function handleNewAccountFormSubmit(formObject) {
        document.getElementById('submitButton').disabled=true;
        google.script.run.withSuccessHandler().processNewAccountForm(formObject);        
      }

      function disableSubmit() {
       document.getElementById('submitButton').disabled=true;
       document.getElementById('submitButton').value='Sending...';
      }

      function showItem(){
        document.getElementById('itemDisplay').style.display ='block';         
        document.getElementById('disabledItem').required = true;         
      };
      function hideItem(){
        document.getElementById('itemDisplay').style.display = 'none';
        document.getElementById('disabledItem').required = false; 
      };
      function showMod(){
        document.getElementById('modDisplay').style.display ='block';
        document.getElementById('disabledMod').required = true; 
      };
      function hideMod(){
        document.getElementById('modDisplay').style.display = 'none';
        document.getElementById('disabledMod').required = false; 
      };

       function showPrice(){
        document.getElementById('priceDisplay').style.display ='block';
        document.getElementById('updatedPrice').required = true; 
      };
      function hidePrice(){
        document.getElementById('priceDisplay').style.display = 'none';
        document.getElementById('updatedPrice').required = false; 
      };

      function businessOtherDisplay(value) {

       if(value === "other") {
          document.getElementById('businessTypeDisplay').style.display = 'block';
        } else {
        document.getElementById('businessTypeDisplay').style.display = 'none';
        };
      };

    </script>
  </body>
</html>

以及处理逻辑的代码

function processNewAccountForm(formObject) {  

  const ui = SpreadsheetApp.getUi();
  const ass = SpreadsheetApp.getActiveSpreadsheet();
  const ss = ass.getActiveSheet();
  const timestamp = Utilities.formatDate(new Date(), "GMT+8", "MM/dd/yyyy HH:mm:ss");
  const userEmail = Session.getActiveUser().getEmail();
  const brandName = formObject.newBrand;

// Add alert to check data entered is correct
  const response = ui.alert('Please confirm the following information is correct:', 
                            '𝗕𝗿𝗮𝗻𝗱 𝗡𝗮𝗺𝗲:  ' + formObject.newBrand + 
                            '\n𝗖𝗼𝘂𝗻𝘁𝗿𝘆:  ' + formObject.country +
                            '\n𝗦𝘁𝗮𝘁𝗲:  ' + formObject.state + 
                            '\n𝗖𝗶𝘁𝘆:  ' + formObject.city +                       
                            '\n𝗕𝘂𝘀𝗶𝗻𝗲𝘀𝘀 𝗧𝘆𝗽𝗲:  ' + formObject.businessType +
                            '\n𝗜𝗻𝘁𝗲𝗴𝗿𝗮𝘁𝗶𝗼𝗻:  ' + formObject.integration + 
                            '\n𝗙𝗶𝗹𝗲 𝗦𝗼𝘂𝗿𝗰𝗲:  ' + formObject.menuSource +
                            '\n𝗗𝗼 𝘆𝗼𝘂 𝗻𝗲𝗲𝗱 𝗮𝗻 𝗶𝘁𝗲𝗺 𝗵𝗶𝗱𝗱𝗲𝗻/𝗱𝗶𝘀𝗮𝗯𝗹𝗲𝗱?:  ' + formObject.disableItemOption +
                            '\n𝗪𝗵𝗶𝗰𝗵 𝗶𝘁𝗲𝗺(𝘀) 𝘀𝗵𝗼𝘂𝗹𝗱 𝗯𝗲 𝗱𝗶𝘀𝗮𝗯𝗹𝗲𝗱?:  ' + formObject.itemDisable +
                            '\n𝗗𝗼 𝘆𝗼𝘂 𝗻𝗲𝗲𝗱 𝗮 𝗺𝗼𝗱𝗶𝗳𝗶𝗲𝗿 𝗵𝗶𝗱𝗱𝗲𝗻/𝗱𝗶𝘀𝗮𝗯𝗹𝗲𝗱?:  ' + formObject.disableModOption +  
                            '\n𝗪𝗵𝗶𝗰𝗵 𝗺𝗼𝗱𝗶𝗳𝗶𝗲𝗿𝘀 𝘀𝗵𝗼𝘂𝗹𝗱 𝗯𝗲 𝗱𝗶𝘀𝗮𝗯𝗹𝗲𝗱?:  ' + formObject.modDisable +  
                            '\n𝗗𝗼 𝘆𝗼𝘂 𝗻𝗲𝗲𝗱 𝘁𝗼 𝘂𝗽𝗱𝗮𝘁𝗲 𝗮 𝗽𝗿𝗶𝗰𝗲?:  ' + formObject.updatePrice +  
                            '\n𝗪𝗵𝗶𝗰𝗵 𝗶𝘁𝗲𝗺/𝗺𝗼𝗱𝗶𝗳𝗶𝗲𝗿 𝗻𝗲𝗲𝗱𝘀 𝗮 𝗽𝗿𝗶𝗰𝗲 𝘂𝗽𝗱𝗮𝘁𝗲?:  ' + formObject.priceUpdate + 
                            '\n𝗔𝗻𝘆 𝗼𝘁𝗵𝗲𝗿 𝗶𝗻𝗳𝗼𝗿𝗺𝗮𝘁𝗶𝗼𝗻 𝗻𝗲𝗲𝗱𝗲𝗱 𝗼𝗻 𝘁𝗵𝗲 𝗺𝗲𝗻𝘂?: ' + formObject.otherUpdates + 
                            '\n𝗔𝗿𝗲 𝘁𝗵𝗲𝗿𝗲 𝘀𝗽𝗲𝗰𝗶𝗮𝗹 𝗶𝗻𝘀𝘁𝗿𝘂𝗰𝘁𝗶𝗼𝗻𝘀/𝗻𝗼𝘁𝗲𝘀 𝗳𝗼𝗿 𝘁𝗵𝗶𝘀 𝗯𝗿𝗮𝗻𝗱?:  ' + formObject.specialInstructions                            
                            , ui.ButtonSet.YES_NO);

    if(response === ui.Button.YES) {
       var lock = LockService.getScriptLock();
  lock.waitLock(60000);
  try {   
    const brandColumn = ss.getRange('D:D');
    const brandValues = brandColumn.getValues();
    let i = 1;
    //      Check for exisiting brand name
    for(i=1; i < brandValues.length; i++) {
        if(brandValues[i].toString().toLowerCase().trim() == brandName.toString().toLowerCase().trim() && ss.getRange(i+1,5).getValue() == 'New Brand'){       
        ui.alert("Brand name already created");
        return;
        }       
    };  
//  Create folder and PDF with build instructions
      const parentFolder = DriveApp.getFolderById("RemovedfolderID");// how does this work with Shared drives? Create and move?
    //      const parentFolder = DriveApp.getFolderById("RemovedfolderID"); < ---- Team drive ID (notworking..)  My folder ->  RemovedfolderID

      const newFolder = parentFolder.createFolder(brandName);
      const docFile = newFolder.createFile(brandName+'.pdf', 
                            '𝗕𝗿𝗮𝗻𝗱 𝗡𝗮𝗺𝗲:  ' + formObject.newBrand + 
                            '\n𝗖𝗼𝘂𝗻𝘁𝗿𝘆:  ' + formObject.country +
                            '\n𝗦𝘁𝗮𝘁𝗲:  ' + formObject.state + 
                            '\n𝗖𝗶𝘁𝘆:  ' + formObject.city +                       
                            '\n𝗕𝘂𝘀𝗶𝗻𝗲𝘀𝘀 𝗧𝘆𝗽𝗲:  ' + formObject.businessType +
                            '\n𝗜𝗻𝘁𝗲𝗴𝗿𝗮𝘁𝗶𝗼𝗻:  ' + formObject.integration + 
                            '\n𝗙𝗶𝗹𝗲 𝗦𝗼𝘂𝗿𝗰𝗲:  ' + formObject.menuSource +
                            '\n𝗗𝗼 𝘆𝗼𝘂 𝗻𝗲𝗲𝗱 𝗮𝗻 𝗶𝘁𝗲𝗺 𝗵𝗶𝗱𝗱𝗲𝗻/𝗱𝗶𝘀𝗮𝗯𝗹𝗲𝗱?:  ' + formObject.disableItemOption +
                            '\n𝗪𝗵𝗶𝗰𝗵 𝗶𝘁𝗲𝗺(𝘀) 𝘀𝗵𝗼𝘂𝗹𝗱 𝗯𝗲 𝗱𝗶𝘀𝗮𝗯𝗹𝗲𝗱?:  ' + formObject.itemDisable +
                            '\n𝗗𝗼 𝘆𝗼𝘂 𝗻𝗲𝗲𝗱 𝗮 𝗺𝗼𝗱𝗶𝗳𝗶𝗲𝗿 𝗵𝗶𝗱𝗱𝗲𝗻/𝗱𝗶𝘀𝗮𝗯𝗹𝗲𝗱?:  ' + formObject.disableModOption +  
                            '\n𝗪𝗵𝗶𝗰𝗵 𝗺𝗼𝗱𝗶𝗳𝗶𝗲𝗿𝘀 𝘀𝗵𝗼𝘂𝗹𝗱 𝗯𝗲 𝗱𝗶𝘀𝗮𝗯𝗹𝗲𝗱?:  ' + formObject.modDisable +  
                            '\n𝗗𝗼 𝘆𝗼𝘂 𝗻𝗲𝗲𝗱 𝘁𝗼 𝘂𝗽𝗱𝗮𝘁𝗲 𝗮 𝗽𝗿𝗶𝗰𝗲?:  ' + formObject.updatePrice +  
                            '\n𝗪𝗵𝗶𝗰𝗵 𝗶𝘁𝗲𝗺/𝗺𝗼𝗱𝗶𝗳𝗶𝗲𝗿 𝗻𝗲𝗲𝗱𝘀 𝗮 𝗽𝗿𝗶𝗰𝗲 𝘂𝗽𝗱𝗮𝘁𝗲?:  ' + formObject.priceUpdate + 
                            '\n𝗔𝗻𝘆 𝗼𝘁𝗵𝗲𝗿 𝗶𝗻𝗳𝗼𝗿𝗺𝗮𝘁𝗶𝗼𝗻 𝗻𝗲𝗲𝗱𝗲𝗱 𝗼𝗻 𝘁𝗵𝗲 𝗺𝗲𝗻𝘂?: ' + formObject.otherUpdates + 
                            '\n𝗔𝗿𝗲 𝘁𝗵𝗲𝗿𝗲 𝘀𝗽𝗲𝗰𝗶𝗮𝗹 𝗶𝗻𝘀𝘁𝗿𝘂𝗰𝘁𝗶𝗼𝗻𝘀/𝗻𝗼𝘁𝗲𝘀 𝗳𝗼𝗿 𝘁𝗵𝗶𝘀 𝗯𝗿𝗮𝗻𝗱?:  ' + formObject.specialInstructions, 
                               MimeType.PDF);       
      const fileURL = docFile.getUrl();

  //  add header row to spreadsheet 
            //   Create Spreadsheet in Brand folder. Activity log.
      const name = brandName + " Activity Log";
      const id = newFolder.getId();
      const resource = {
        title: name,
        mimeType: MimeType.GOOGLE_SHEETS,
        parents: [{id: id}]      
      };

      const fileJson = Drive.Files.insert(resource);
      const fileId = fileJson.id;

      const lastRow = ss.getLastRow();

        const newEntry = [
         lastRow, 
         timestamp, 
         timestamp, 
         formObject.newBrand, 
         'New Brand', 
         formObject.businessType, 
         formObject.integration, 
         '=HYPERLINK("'+formObject.menuSource+'")', 
         userEmail,
         fileURL,
         ,
         ,
         ,
         ,
         formObject.city,
         formObject.state,
         formObject.country,
         fileId         

    ];   

    const newSheet = SpreadsheetApp.openById(fileId);    
    const sheetRange = newSheet.getSheetByName("Sheet1").getRange(1,1,1,18);  
    const headers = [
      ['𝗜𝗻𝗱𝗲𝘅',
       '𝗕𝗿𝗮𝗻𝗱 𝗼𝗿𝗶𝗴𝗶𝗻𝗮𝗹 𝗰𝗿𝗲𝗮𝘁𝗲 𝗱𝗮𝘁𝗲',
       '𝗧𝗶𝗺𝗲 𝗜𝗻',
       '𝗕𝗿𝗮𝗻𝗱 𝗡𝗮𝗺𝗲',
       '𝗥𝗲𝗾𝘂𝗲𝘀𝘁 𝗿𝗲𝗮𝘀𝗼𝗻',
       '𝗕𝘂𝘀𝗶𝗻𝗲𝘀𝘀 𝘁𝘆𝗽𝗲',
       '𝗜𝗻𝘁𝗲𝗴𝗿𝗮𝘁𝗶𝗼𝗻',
       '𝗠𝗲𝗻𝘂 𝗦𝗼𝘂𝗿𝗰𝗲',
       '𝗖𝗿𝗲𝗮𝘁𝗲𝗱 𝗯𝘆:',
       '𝗠𝗲𝗻𝘂 𝗜𝗻𝘀𝘁𝗿𝘂𝗰𝘁𝗶𝗼𝗻𝘀',
       '𝗔𝘀𝘀𝗶𝗴𝗻𝗲𝗱 𝘁𝗼?',
       '𝗧𝗶𝗺𝗲 𝗢𝘂𝘁',
       '𝗖𝗼𝗺𝗽𝗹𝗲𝘅𝗶𝘁𝘆 𝗥𝗮𝘁𝗶𝗻𝗴',
       '𝗚𝗼 𝗹𝗶𝘃𝗲 𝗱𝗮𝘁𝗲 𝗮𝗻𝗱 𝘁𝗶𝗺𝗲',
       '𝗖𝗶𝘁𝘆',
       '𝗦𝘁𝗮𝘁𝗲/𝗣𝗿𝗼𝘃𝗶𝗻𝗰𝗲',
       '𝗖𝗼𝘂𝗻𝘁𝗿𝘆',
       '𝗔𝗰𝘁𝗶𝘃𝗶𝘁𝘆 𝗟𝗼𝗴']
      ];

     sheetRange.setValues(headers);

//    Add data to last row in main tracker  
      ss.appendRow(newEntry);

//  Copy data to spreadsheet brand 
      const activitySheet = newSheet.getSheetByName("Sheet1") 
      activitySheet.appendRow(newEntry);
//    Flush changes before releasing lock
      SpreadsheetApp.flush();
  } catch(e) {
    ui.alert("System is Busy, Please try again in a moment.");
    return
    } finally {

    lock.releaseLock();

    return
  }

  } else {
//     action to take if info is incorrect? or No is clicked 
  };  

};

我知道根据 Cooper 和其他人在这里这里的帖子,多个触发器是一个已知问题,但我似乎无法将它们用于我的解决方案。

提前感谢您的任何想法。

这些是我对对话框所做的更改:

 <input id="btn1" type="button" value="Submit" onClick="handleNewAccountFormSubmit(this.parentNode);" />
    <input type="button" value="Cancel" onclick="google.script.host.close()">
    </div>
  </form>  
  <script>

      function handleNewAccountFormSubmit(formObject) {
        document.getElementById('btn1').disabled=true;
        google.script.run.processNewAccountForm(formObject);        
      }

我并不是说你的方式是错误的。 这就是我会尝试这样做的方式。 但实际上,这是一个相当大的对话,您只需要深入研究并弄清楚。 如果我这样做并且遇到了很多问题,我可能会从一个更简单的版本开始,让它工作,然后慢慢添加更多功能。

这实际上是一个相当困难的时间,在两个运行时之间的转换中间编写代码。 我刚刚注意到我在 ES5 的某些领域丢失了我的内容辅助,但它们现在可以在 ES6 中工作,所以事情可能很难处理。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM