【问题标题】:How to read the selected value of a dropdown for use in a second dropdown如何读取下拉列表的选定值以在第二个下拉列表中使用
【发布时间】:2022-08-15 05:28:50
【问题描述】:

我正在为谷歌表格制作侧栏。我有一个 html 页面。我发送的数据分为两部分。我有帐户,每个帐户都有属性。

{
      \"name\": \"accountSummaries/223471136\",
      \"account\": \"accounts/223471136\",
      \"displayName\": \"G4MeasurmentTesting\",
      \"propertySummaries\": [
        {
          \"property\": \"properties/307799514\",
          \"displayName\": \"Ga4CsharpTesting\",
          \"propertyType\": \"PROPERTY_TYPE_ORDINARY\",
          \"parent\": \"accounts/223471136\"
        }
      ]
    },

我想要做的是在我的菜单中显示两个下拉菜单,第一个是帐户,第二个是帐户下的属性。

我的问题是当用户选择第一个选择时如何重新加载第二个选择?

我给第一个选择了一个名称 <select id=\"account\" name=\"account\"> 然后尝试使用 accounts[account.selected].propertySummaries.length; 为第二个加载数据

它不起作用,因为我看到ReferenceError: account is not defined 这对我来说意味着它没有看到选择的名称。

必须有一种方法来检测在第一个选择中选择了哪个项目对吗?

<!DOCTYPE html>
<html>
  <head>
    <base target=\"_top\">
  </head>
  <body>
    
    <? var accounts = listAccounts();      ?>    
    <? const item = accounts[1];?> 
    
<label>Account: </label>
<select id=\"account\" name=\"account\">
  <option></option>
    <?  for (var i = 0; i < accounts.length; i++) { ?>
        <option value=\"<?= i ?>\"><?= accounts[i].displayName ?></option>
    <? } ?>  
</select>
    
<!-- Not working  -->
<label>property: </label>
<select id=\"view\" name=\"view\">
  <option></option>
    <?  for (var i = 0; i < accounts[account.selected].propertySummaries.length; i++) { ?>
        <option><?= accounts[account.selected].propertySummaries[i].displayName ?></option>
    <? } ?>  
</select>    
<!-- Not working  -->

   <?= accounts ?> <input type=\"button\" value=\"Close\" onclick=\"google.script.host.close()\" />
  </body>
</html>

上面的代码给出了一个错误ReferenceError: account is not defined

代码.gs

function onOpen() {

  var ui = SpreadsheetApp.getUi();

   // Or DocumentApp or FormApp.
  ui.createMenu(\'GA Menu\')
      .addItem(\'Alert Test\', \'ShowMessage\')
      .addSeparator()
      .addSubMenu(ui.createMenu(\'Sub-menu\')
      .addItem(\'Second item\', \'ShowPrompt\'))      
      .addItem(\'Show sidebar\', \'showSidebar\')    
      .addToUi();

  
}

// https://developers.google.com/apps-script/guides/dialogs?hl=en#code.gs_1

function showSidebar() {

 var html = HtmlService
      .createTemplateFromFile(\'Page\')
      .evaluate(); 
  SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
    .showSidebar(html);
}

如果我通过指定说 0 来强制它,它确实有效,所以问题不在于它能够读取我的数据。因此,问题中的数据表单不是问题,而是链接两个选择。

<label>property: </label>
<select id=\"view\" name=\"view\">
    <?  for (var i = 0; i < accounts[0].propertySummaries.length; i++) { ?>
        <option><?= accounts[0].propertySummaries[i].displayName ?></option>
    <? } ?>  
</select>    

更新尝试 2

另一个想法是尝试使用文档来抓取它

<? const account = document.getElementById(\'account\'); ?>

哪个也没用,它只是给了我

ReferenceError: document is not defined详情

  • 您的代码引用了account.selected,但我没有看到任何地方定义了account,就像错误所说的那样。
  • <select id=\"account\" name=\"account\"> <--- 它是我试图从中检测到的选择下拉列表的名称。我已经编辑了我的问题,以便更清楚我为解决这个问题所做的努力。
  • 您只显示最初加载页面时运行的模板化脚本。你的&lt;script&gt; 在哪里显示事件处理程序document.getElementById(\"account\").addEventListener(\"change\", myFunction)?并显示您的服务器功能,该功能显示模板填写的侧边栏。
  • @TheWizEd 看到现在我们正在取得一些进展。我没有那个。我确实尝试了 document.getElementById,您可以在第二次更新中看到它,但也显示错误。添加code.gs检查编辑请
  • accounts 在您的模板中是如何定义的?而&lt;?= accounts[i].displayName ?&gt;evaluate 之前需要在showSidebar 中使用html.accounts = accounts;。给我一点时间,我会做一个模型,我会做什么。

标签: javascript google-apps-script


【解决方案1】:

修改点:

  • 为了通过第一个下拉列表将值设置到第二个下拉列表,使用 JavaScript。
    • 在您的 HTML 中,第二个下拉列表是使用 JavaScript 使用第一个下拉列表的值创建的。
  • 使用Google Apps Script 的HTML 模板时,使用evaluate() 方法运行计算时,处理成本变高。 Ref
  • 在 HTML 模板的情况下,当加载 HTML 时,evaluate() 创建的 HTML 被放置为静态。因此,您的 HTML 中的 account 无法使用。我认为这是您当前问题的原因。

当这些点反映在你的脚本中时,下面的修改怎么样?我认为在这种情况下,有几种方法。

修改后的脚本:

HTML端:Page.html

<label>Account: </label>
<select id="account" name="account" onchange="sample(this)"><?!= firstOptions ?></select>

<label>property: </label>
<select id="view" name="view"><?!= secondOptions ?></select>

<input type="button" value="Close" onclick="google.script.host.close()" />

<script>    
const accounts = JSON.parse(<?= accounts ?>);

function sample(e) {
  const select = document.getElementById("view");
  select.length = 0;
  const { propertySummaries } = accounts[e.value];
  propertySummaries.forEach(({ displayName }, i) => {
    const option = document.createElement("option");
    option.text = displayName;
    option.value = i;
    select.appendChild(option);
  });
}
</script>
  • 通过加载accounts的值,在加载HTML之后,就可以使用这个值了。

Google Apps 脚本端:Code.gs

function showSidebar() {
  const accounts = listAccounts();
  const html = HtmlService.createTemplateFromFile('Page');
  html.accounts = JSON.stringify(accounts);
  html.firstOptions = accounts.map(({ displayName }, i) => `<option value="${i}">${displayName}</option>`).join("");
  html.secondOptions = accounts[0].propertySummaries.map(({ displayName }, i) => `<option value="${i}">${displayName}</option>`).join("");
  SpreadsheetApp.getUi().showSidebar(html.evaluate());
}
  • 在这种情况下,它假设 listAccounts() 返回如下值。

      [{
          "name": "accountSummaries/223471136",
          "account": "accounts/223471136",
          "displayName": "G4MeasurmentTesting",
          "propertySummaries": [
            {
              "property": "properties/307799514",
              "displayName": "Ga4CsharpTesting",
              "propertyType": "PROPERTY_TYPE_ORDINARY",
              "parent": "accounts/223471136"
            }
          ]
        },
        ,
        ,
        ,
      ]
    
  • showSidebar() 运行时,您可以看到第一个下拉列表。当您更改第一个下拉列表时,会更改第二个下拉列表。

  • 更新:我注意到第一次打开侧边栏时,未设置第二个下拉列表的初始值,而第一个下拉列表的初始值是第一个元素。为此,我更新了脚本。如果不需要将初始值设置为第二个下拉列表,请使用以前的版本。

笔记:

  • 这是一个用于解释结果的简单示例脚本。所以,请根据您的实际情况进行修改。

参考:

添加:

对于Should I / can I place my listAccounts method in the page.html file along with the sample method you showed.,当您的listAccounts() 不包含Google Apps Script 的方法时,您可以使用以下示例脚本。

示例脚本:

HTML端:Page.html

<label>Account: </label>
<select id="account" name="account" onchange="sample(this)"></select>

<label>property: </label>
<select id="view" name="view"></select>

<input type="button" value="Close" onclick="google.script.host.close()" />

<script>

// Please set your value here.
function listAccounts() {
  return [{
      "name": "accountSummaries/223471136",
      "account": "accounts/223471136",
      "displayName": "G4MeasurmentTesting",
      "propertySummaries": [
        {
          "property": "properties/307799514",
          "displayName": "Ga4CsharpTesting",
          "propertyType": "PROPERTY_TYPE_ORDINARY",
          "parent": "accounts/223471136"
        }
      ]
    },
    ,
    ,
    ,
  ];
}

const accounts = listAccounts();

function sample(e) {
  const select = document.getElementById("view");
  select.length = 0;
  const {propertySummaries} = accounts[e.value];
  propertySummaries.forEach(({displayName}, i) => {
    const option = document.createElement("option");
    option.text = displayName;
    option.value = i;
    select.appendChild(option);
  });
}

// When HTML is opened, this function is run. And, the initial values are put to the 1st and 2nd dropdown list.
function init() {
  accounts.forEach(({displayName}, i) => {
    const option = document.createElement("option");
    option.text = displayName;
    option.value = i;
    account.appendChild(option);
  });
  accounts[0].propertySummaries.forEach(({displayName}, i) => {
    const option = document.createElement("option");
    option.text = displayName;
    option.value = i;
    view.appendChild(option);
  });
}

init();
</script>
  • 在这种情况下,如果 Google Apps Script 的方法包含在 listAccounts() 中,则无法使用此脚本。因为 Google Apps 脚本无法使用客户端浏览器运行。请注意这一点。

Google Apps 脚本端:Code.gs

function showSidebar() {
  const html = HtmlService.createHtmlOutputFromFile('Page');
  SpreadsheetApp.getUi().showSidebar(html);
}
  • 在这种情况下,在 Google Apps 脚本端,仅打开侧边栏。

【讨论】:

  • 它就像一个魅力。我有个问题。我应该/我可以将我的 listAccounts 方法与您展示的示例方法一起放在 page.html 文件中。
  • @DaImTo 感谢您的回复。我很高兴你的问题得到了解决。关于您的其他问题,我又添加了一个示例脚本。你能确认一下吗?
  • 确切地!我试图添加它,但错过了它必须在 HTML 文件中声明。
  • html文件中的所有内容在性能方面有什么不同吗?在开发方面,将所有东西都放在一个地方对我来说感觉更整洁。
  • @DaImTo 感谢您的回复。使用HTML模板时,增加了evaluate()的处理成本。而且,当使用evaluate() 运行类似for 循环的计算时,处理成本会变高。当使用google.script.run 时,处理成本也会变高。但是,如果需要通过 Google Apps 脚本端动态发送和获取值,则需要使用此方法。在您的情况下,当您可以在没有 Google Apps 脚本的情况下在 HTML 中实现目标时,我认为当所有脚本都包含在一个 HTML 文件中时,处理成本将变得最低。
【解决方案2】:

我看到@Tanaike 打败了我,但正如我所承诺的那样,我提供了一个模型,说明如何通过模板化 HTML 以及使用 google.script.run 来做到这一点。

首先是 Code.gs 中的服务器端代码。我在模板和客户端&lt;script&gt; 中都使用listAccounts()

然后我将展示如何修改 html 代码以包含事件处理程序。

代码.gs

function showTest() {
  var html = HtmlService.createTemplateFromFile("HTML_Test");
  html.accounts = listAccounts();
  SpreadsheetApp.getUi().showModalDialog(html.evaluate(),"Test");
}

function include(filename) {
  return HtmlService.createHtmlOutputFromFile(filename)
      .getContent();
}

// Mock up data
function listAccounts() {
  let accounts = [ {account: 11111, displayName: "name11111", propertySummaries: [ {displayName: "prop11111"}, {displayName: "prop11112"} ]}, 
                    {account: 22222, displayName: "name22222", propertySummaries: [ {displayName: "prop22222"}, {displayName: "prop22223"} ]},
                    {account: 33333, displayName: "name33333", propertySummaries: [ {displayName: "prop33333"}, {displayName: "prop33334"} ]}];
  return accounts;
}

HTML_Test.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    
    <label>Account: </label>
    <select id="account" name="account">
      <?  for (var i = 0; i < accounts.length; i++) { ?>
        <option value="<?= i ?>"><?= accounts[i].displayName ?></option>
      <? } ?>  
    </select>
    
    <label>property: </label>
    <select id="view" name="view">
    </select>    

    <input type="button" value="Close" onclick="google.script.host.close()" />
    <script>
      var accounts = null;
      function accountOnChange() {
        let i = document.getElementById("account").selectedIndex;
        let account = accounts[i];
        let select = document.getElementById("view");
        let options = select.options;
        for( i=options.length; i>0; i-- ) {
          options.remove(i-1);
        }
        account.propertySummaries.forEach( property => {
            let option = document.createElement("option");
            option.text = property.displayName;
            select.options.add(option);
          }
        );

      }
      (function() {
        try {
          document.getElementById("account").addEventListener("change",accountOnChange);
          google.script.run.withSuccessHandler(
            function(value) {
              accounts = value;
              let select = document.getElementById("view");
              accounts[0].propertySummaries.forEach( property => {
                  let option = document.createElement("option");
                  option.text = property.displayName;
                  select.options.add(option);
                }
              );
            }
          ).listAccounts();
        }
        catch(err) {
          alert(err);
        }
      })();
    </script>
  </body>
</html>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-24
    • 2015-05-06
    • 2015-10-05
    相关资源
    最近更新 更多