【问题标题】:Google Apps Script Web App - crossing server side calls(?)Google Apps Script Web App - 跨越服务器端调用(?)
【发布时间】:2016-06-15 18:09:22
【问题描述】:

我正在编写一个 Web 应用程序,并希望将更新实时写回服务器。在客户端,我运行此功能并捕获每个输入。输入当前都是复选框。问题是我可以比脚本更快地检查这些框,因此最终会得到意想不到的结果。所以我需要在选择之间减慢用户的速度,或者确保每个服务器调用在下一个调用开始之前完成。我该怎么做?

这是客户端脚本部分的开始。还有其他功能,例如成功和失败处理程序。

<script>
$("form").change(function(e) {

if (e.target.type && e.target.type === 'checkbox') {
  //A checkbox was changed, so act on it
  var name = e.target.value.substr(0,e.target.value.lastIndexOf("_"));
  var position = e.target.value.substr(e.target.value.lastIndexOf("_") + 1);
  var passArray = [name, position];

  if (e.target.checked) {
    //Add the value to the person's Assigned Position
    google.script.run.withSuccessHandler(editChangeReturnedFromServer).withFailureHandler(editFailed).setWSAssignedPosition(passArray);
  } else {
    //Remove the value from the peson's Assigned Position
    google.script.run.withSuccessHandler(editChangeReturnedFromServer).withFailureHandler(editFailed).clearWSAssignedPosition(passArray);
  }
  return;
}

 ...(other functions)...
 </script>

如果用户快速选中复选框,则对服务器端 setWSAssignedPosition() 函数的调用似乎会交叉甚至重复,因为所有正确的值都没有添加到电子表格中,或者添加了相同的多个副本。如果我在google.script.run... 行之前使用alert('I am changing that'); 减慢最终用户的速度,那么一切正常。实际上,我看到电子表格中的一行被后来出现的条目替换。但这很烦人,很快就变得烦人了。

我对提交或应用按钮不感兴趣。我真的希望这能即时发生。

服务端函数是:

//Set the Assigned Position for the person passed to the function
function setWSAssignedPosition(passedArray){

  var name = passedArray[0];
  var position = passedArray[1];

  //Get the entries from the filled out Position Requests in the appropriate sheet
  var validWSRequests = getValidWSRequests();
  var foundWSRequest = false;

  for (i in validWSRequests) {
    if (validWSRequests[i].participantName === name){
      ws_sheet.getRange(validWSRequests[i].sheetrow + 2 , ws_headers[0].indexOf("Assigned Position") + 1).setValue(position);
      foundWSRequest = true;
      break;

    }
  }

  if (!foundWSRequest) {
    var WSOthersAssigned = getRowsData(ws_norequests_sheet);
    var WSOthersAssigned_headers = ws_norequests_sheet.getRange(1, 1, 1, ws_norequests_sheet.getLastColumn()).getValues();
    //Get the first empty row on the sheet for those who didn't fill out the form in case we need it.
    var firstEmptyRow = getFirstEmptyRowWholeRow(ws_norequests_sheet);

    if (WSOthersAssigned.length < 1){
        //No records exist at all, so add the first
          ws_norequests_sheet.getRange(firstEmptyRow, WSOthersAssigned_headers[0].indexOf("Assigned Position") + 1).setValue(position); 
          ws_norequests_sheet.getRange(firstEmptyRow, WSOthersAssigned_headers[0].indexOf("PARTICIPANT NAME") + 1).setValue(name); 
     }else {

      for (i in WSOthersAssigned){
        var seeme = i;
        if (WSOthersAssigned[i].participantName === name) {
          //Found a record so edit it
          ws_norequests_sheet.getRange(WSOthersAssigned[i].sheetrow + 2 , WSOthersAssigned_headers[0].indexOf("Assigned Position") + 1).setValue(position);
          break;

        } else {
          //No record found, so append it
          ws_norequests_sheet.getRange(firstEmptyRow, WSOthersAssigned_headers[0].indexOf("Assigned Position") + 1).setValue(position); 
          ws_norequests_sheet.getRange(firstEmptyRow, WSOthersAssigned_headers[0].indexOf("PARTICIPANT NAME") + 1).setValue(name);
          break;

        }
      }
  }
  }
// This didn't help  
//  SpreadsheetApp.flush();
return [name, true, position];

}

ws_norequests_sheetws_norequests_sheet 是全局定义的,以便在存储这些项目的电子表格中获取适当的工作表。根据初始来源,数据保存在两张纸中的一张中。我目前正在测试所有数据进入第二张表的位置,因为这些复选框都堆叠在一起,因此可以快速访问

【问题讨论】:

    标签: google-apps-script


    【解决方案1】:

    这就是异步服务器调用的本质。您始终需要注意,呼叫可能不会按照您发送它们的顺序返回。您始终需要小心并使用成功和失败回调来正确响应。

    有几种方法可以处理您的问题(以您想要的方式),其中大多数涉及阻止用户触发表单元素的更改操作,然后仅在服务器调用返回后才允许他们这样做。

    一种更简单的方法是在更改时禁用表单元素并在回调中重新启用它们。

    所以首先在更改时禁用它们:

    $("form").change(function(e) {
      if (e.target.type && e.target.type === 'checkbox') {
        $("input[type=checkbox]", "form").attr("disabled", "disabled");
      //Your change code here  
      }     
    }
    

    然后在您的成功和失败处理程序中,重新启用它们:

    function editChangeReturnedFromServer(response) {
      //your code here to work with server data
      $("input[type=checkbox]", "form").removeAttr("disabled");
    }
    function editFailed(error) {
      //your code here to handle error
      $("input[type=checkbox]", "form").removeAttr("disabled");
    }
    

    其他选项是使用 jQuery UI 之类的东西来创建一个模式对话框,该对话框会阻止用户单击选项,您可能会在成功时将其破坏。这很好,因为它非常清楚地向用户表明正在发生某些事情,而禁用输入对他们来说可能并不那么明显。

    基本上,您需要确保用户在前一个更改完成之前不能触发另一个更改。或者,理论上您可以使用一个全局变量来跟踪未处理的复选框值,并使用另一个来跟踪服务器调用是否正在进行中,但这对于这么简单的事情来说非常复杂。

    坦率地说,从 UI 的角度来看,我并不真正同意您的方法。复选框的预期行为是它收集用户的输入并仅在单击提交按钮时将其发送到服务器。作为最终用户,我希望在“提交”某些内容时等待,而不是在选中一个框时等待。不得不等待任何时间(甚至几分之一秒)都会让我很恼火。我宁愿检查几个选项并等待更长的提交。

    根据对返回值的实际执行情况,更优雅的解决方案可能是立即处理客户端的操作,而不涉及服务器。仅在用户完成选择后才向其发送数据。

    例如,如果选中一个框应该向用户显示 3 个输入,您可以:

    $("#check1").click(function(){
      $(".showOnCheck1").toggle(this.checked);
    });
    .showOnCheck1 {display:none;}
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <p>
      <input type="checkbox" id="check1" value="check1"><label for="check1">Check box 1</label>
    </p>
    <p class="showOnCheck1">
      <label for="text1">Text Box 1</label><input type="text" id="text1">
    </p>
    <p class="showOnCheck1">
      <label for="text2">Text Box 2</label><input type="text" id="text2">
    </p>
    <p class="showOnCheck1">
      <label for="text3">Text Box 3</label><input type="text" id="text3">
    </p>

    正如您在上面看到的那样,根本没有延迟,也不需要减慢用户速度或等待服务器,但这确实取决于您对响应所做的操作。您可以在客户端放置的逻辑越多,用户等待服务器的时间就越少。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-14
      • 1970-01-01
      • 2019-07-21
      • 1970-01-01
      • 2019-02-01
      • 2016-08-10
      • 2012-07-21
      相关资源
      最近更新 更多