我可以使用 HTML 侧边栏调用 Google Appscript 中的函数吗?

Can I use HTML sidebar to call a function in Google Appscript?

我需要使用边栏获取一些变量,然后使用 AppScript 在 sheet 的最后一行打印。所以,我试图使用这段代码:

Sidebar.HTML

<!DOCTYPE html>
<html>

<head>
  <base target="_top">
</head>

<body>
  <p>Name: <input type="text" name="txtName" /></p>
  <p>Date: <input type="text" name="txtDate" /></p>
  <p>Value: <input type="text" name="txtValue" /></p>
  <button onclick="doSomething()"> submit </button>
  <script>
    function doSomething() {
      google.script.run.withFailureHandler(myFunction(document.getElementById("txtName", "txtDate", "txtValue").value))
    }
  </script>

</body>

</html>

Code.js

function myFunction(name = "", date = "", value = "") {
  var ss = SpreadsheetApp.getActive()
  var sheet = ss.getSheetByName("1")

  var lr = sheet.getLastRow() + 1

  sheet.getRange(lr, 1).setValue(name)
  sheet.getRange(lr, 2).setValue(date)
  sheet.getRange(lr, 3).setValue(value)
}

function openDialog() {
  var html = HtmlService.createHtmlOutputFromFile("Sidebar");
  html.setTitle("Form");
  SpreadsheetApp.getUi().showSidebar(html);
}

但是没有用。当我点击按钮时没有任何反应。我是 HTML 的新手,所以,我可以做些什么来修复它?

看起来你几乎做对了,除了 2 件事,都在 Sidebar.HTML 文件中 —

  1. 鉴于您正在使用 getElementById,您还需要在 input 字段中添加一个 id 标签
  2. google.script.run.withFailureHandler 的实现似乎有点不对劲。您可以阅读 official documentation 了解更多信息
    • 您也可以完全跳过使用 withFailureHandler,只需使用 google.script.run.myFunction(...)

最后的工作代码供参考—

<!DOCTYPE html>
<html>

<head>
  <base target="_top">
</head>

<body>
  <p>Name: <input type="text" name="txtName" id="txtName" /></p>
  <p>Date: <input type="text" name="txtDate" id="txtDate" /></p>
  <p>Value: <input type="text" name="txtValue" id="txtValue" /></p>
  <button onclick="doSomething()"> submit </button>
  <script>
    function doSomething() {
      google.script.run.withFailureHandler()
      .myFunction(document.getElementById("txtName").value,document.getElementById("txtDate").value,document.getElementById("txtValue").value)
    }
  </script>

</body>

</html>

通过边栏中的名称动态调用服务器端函数

您可以从边栏调用许多服务器端功能。我有一个电子表格,用于回答有关 SO 的问题,其中大部分脚本位于五个文件中,它们位于我的全局散列 table 键 sbfiles 下。目前他们是 ag1,ag2,ag3,ag4,zkeepers。

select 下拉列表中的名称是动态的,每次重新加载边栏时都会读取。

当我使用以下函数加载边栏时:

有了这个功能:

function showToolsSideBar() {
  var userInterface = HtmlService.createTemplateFromFile('toolsSideBar').evaluate().setTitle('SO Tools');
  SpreadsheetApp.getUi().showSidebar(userInterface);
}

这将加载具有以下四个按钮的 toolsSideBar.html:

 <br /><strong>Test Buttons</strong>
   <br /><input type="button" value="run1()" onClick="execFunc1();" /><select id="func1"></select>
   <br /><input type="button" value="run2()" onClick="execFunc2();" /><select id="func2"></select>
   <br /><input type="button" value="run3()" onClick="execFunc3();" /><select id="func3"></select>
   <br /><input type="button" value="run4()" onClick="execFunc4();" /><select id="func4"></select>
   <hr />

这是一个模板化的 html 文件,其中包含以下行:

<?!= include('sbscript') ?>

并且sbscript.html包含:

$(function(){
    google.script.run
    .withSuccessHandler(function(vA){
      let idA=["func1","func2","func3","func4"];
      idA.forEach(function(id){
        updateSelect(vA,id);
      });
    })
    .getProjectFunctionNames();
    var elem = document.getElementById("permnotes1");
    var v = localStorage.getItem(elem.name);
    if(v) {elem.value = v;}
    elem.addEventListener("change",saveText);
    //console.log('elem.name: %s',elem.name);
  })

调用此服务器端函数 getProjectFunctionNames():

function getProjectFunctionNames() {
  const vfilesA=getGlobal('sbfiles').split(',');
  const scriptId="script id";
  const url = "https://script.googleapis.com/v1/projects/" + scriptId + "/content?fields=files(functionSet%2Cname)";
  const options = {"method":"get","headers": {"Authorization": "Bearer " +  ScriptApp.getOAuthToken()}};
  const res = UrlFetchApp.fetch(url, options);
  let html=res.getContentText();
  //SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html), "Project Functions");
  let data=JSON.parse(res.getContentText());
  let funcList=[];
  let files=data.files;
  files.forEach(function(Obj){
    if(vfilesA.indexOf(Obj.name)!=-1) {
      if(Obj.functionSet.values) {
        Obj.functionSet.values.forEach(function(fObj){
          funcList.push(fObj.name);
        });
      }
    }
  });      //SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(funcList.join(', ')), "Project Functions");
  return funcList;
}

此函数使用应用程序脚本 api 读取我的全局哈希 table 密钥 sbfiles 中的所有文件。该列表返回到 javascript 函数的成功处理程序,并使用此函数使用此脚本为 html 文件中的四个按钮中的每一个加载 select 框:

function updateSelect(vA,id){
    var id=id || 'sel1';
    var select = document.getElementById(id);
    select.options.length = 0; 
    vA.unshift("");
    for(var i=1;i<vA.length;i++){
      select.options[i] = new Option(vA[i],vA[i]);
    }
  }

其中returns我们回到这个:

 <br /><strong>Test Buttons</strong>
   <br /><input type="button" value="run1()" onClick="execFunc1();" /><select id="func1"></select>
   <br /><input type="button" value="run2()" onClick="execFunc2();" /><select id="func2"></select>
   <br /><input type="button" value="run3()" onClick="execFunc3();" /><select id="func3"></select>
   <br /><input type="button" value="run4()" onClick="execFunc4();" /><select id="func4"></select>
   <hr />

您可以通过四个 select 下拉菜单中的任何一个 select 任何函数名称,然后按下左侧的按钮,您将调用 select 下拉菜单中的函数在按钮的右侧:

function execFunc1() {
    var funcname=$('#func1').val();
    google.script.run.executeFunctionByName(funcname);
  }
  function execFunc2() {
    var funcname=$('#func2').val();
    google.script.run.executeFunctionByName(funcname);
  }
  function execFunc3() {
    var funcname=$('#func3').val();
    google.script.run.executeFunctionByName(funcname);
  }
  function execFunc4() {
    var funcname=$('#func4').val();
    google.script.run.executeFunctionByName(funcname);
  }

然后将调用此函数:

function executeFunctionByName(func) {
  this[func]();
}

依次调用在 sbfiles 的散列 table 键中找到的文件中的函数。

这使我可以在侧边栏调用我正在开发的任何这些文件中的任何函数,而我正在开发它们,只需 select 为每个函数调用它们中的每一个四个可能的按钮,最终调用函数,其名称出现在与该函数关联的 select 框中。这使得测试我当前正在处理的代码变得很方便。

This is a really handy feature when developing new code to be able to have access to the most recent code right in the side. It's really handy when developing new dialog code because you can reissue a call to the function you are working on from the sidebar:

演示: