include(page) 函数,用于脚本未加载 DOM 引用

include(page) function for script not loading DOM references

提问人:J_da_grey 提问时间:4/4/2023 最后编辑:J_da_grey 更新时间:4/4/2023 访问量:52

问:

使用 Google Apps 脚本 HtmlService 类生成一些输出。这个特定案例适用于 Google 表格侧边栏,但我在用作 Web 应用程序时也会遇到它。

我遇到了一个问题,如果我将脚本直接放入 html 文件中,DOM 引用可以正常工作。一旦我把它们放到 scriptlet 中,它就会抛出一个错误;

未捕获的 TypeError:无法读取 null 的属性(读取“值”)

下面是 html 表单和 scriptlet:

<body>
  <?!= include('LOGO') ?>
  <form id="add">
    <div><input id=" fName" type="text" /> First Name</div>
    <div><input id="lName" type="text" /> Last Name</div>
    <div><input id="empId" type="text" /> ID#</div>
    <div><input id="job" type="text" /> Job</div>
    <div><input id="wage" type="text" /> Wage</div>
    <div id="job2" type="text" style="display: none"><input id="jobtwo" type="text" /> Job 2</div>
    <div id="wage2" type="text" style="display: none"><input id="wagetwo" type="text" /> Wage 2</div>
    <div id="job3" type="text" style="display: none"><input id="jobthree" type="text" /> Job 3</div>
    <div id="wage3" type="text" style="display: none"><input id="wagethree" type="text" /> Wage 3</div>
    <div>
      <button id="enter" type="button" onclick="addRecord()"> Enter</button>
      <button id="more" type="button" onclick="moreJobs()"> + Job</button>
      <button id="less" type="button" onclick="lessJobs()"> - Job</button>
      <button id="close" type="button" onclick="closeSide()"> Close</button>
    </div>
  </form>

  <?!= include("AddEmpJS") ?>
</body>

这是包含函数引用的 AddEmpJS.html;

<script>
    function addRecord() {
      let fname = document.getElementById("fName").value
      let lname = document.getElementById("lName").value
      let empId = document.getElementById("empId").value
      let job = document.getElementById("job").value
      let wage = document.getElementById("wage").value
      let job2 = document.getElementById("jobtwo").value
      let wage2 = document.getElementById("wagetwo").value
      let job3 = document.getElementById("jobthree").value
      let wage3 = document.getElementById("wagethree").value
      let record = [
        fname, lname, empId, job, wage, job2, wage2, job3, wage3
      ]
      console.log(record)
      google.script.run.addEmpRecord(record);
      document.getElementById("add").reset()
    }

    function moreJobs() {
      if(document.getElementById("job2").style.display == "none") {
        document.getElementById("job2").style.display = "block"
        document.getElementById("wage2").style.display = "block"
        return "job 2 showing"
      }
      if(document.getElementById("job3").style.display == "none") {
        document.getElementById("job3").style.display = "block"
        document.getElementById("wage3").style.display = "block"
        return "job 3 showing"
      }
    }

    function lessJobs() {
      if(document.getElementById("job3").style.display == "block") {
        document.getElementById("job3").style.display = "none"
        document.getElementById("wage3").style.display = "none"
        return "removing job 3"
      }      
      if(document.getElementById("job2").style.display == "block") {
        document.getElementById("job2").style.display = "none"
        document.getElementById("wage2").style.display = "none"
        return "removing job 2"
      }
    }

    function closeSide() {
      google.script.host.close()
    }

   </script>

服务器端的 include 函数,

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

调用侧边栏的 UI,

function addEmp() {
  var page = HtmlService.createTemplateFromFile("AddEmp").evaluate();
  page.setTitle("💼 Add Employee")
  ui.showSidebar(page)

和整个剧本,

const ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("EMPLOYEES");
const setup = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SETUP");
const summary = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SUMMARY");
const ui = SpreadsheetApp.getUi();

function onOpen(e) {
  ui.createMenu("👔 Employees")
    .addItem("💼 Add Employee", "addEmp")
    .addItem("✨ Edit Employee", "editEmp")
    .addItem("🔥 Del Employee", "delEmp")
    .addToUi();
  ui.createMenu("📋 Departments")
    .addItem("➕ Add Department", "addDept")
    .addItem("🔧 Edit Department", "editDept")
    .addItem("➖ Del Department", "delDept")
    .addToUi();
  ui.createMenu("⌚ Schedule")
    .addItem("📅 Add Week", "addSched")
    .addItem("🔎 Update Summary Tab", "updateTabs")
    .addToUi();    
  updateTabs();
}

function updateTabs() {
  var tabList = SpreadsheetApp.getActiveSpreadsheet().getSheets().map((s) => s.getName())
    .filter((s) => s != "SETUP" && s != "EMPLOYEES" && s != "TEMPLATE" && s != "SUMMARY")
  Logger.log(tabList)
  var rule = SpreadsheetApp.newDataValidation().requireValueInList(tabList)
  var dropDown = summary.getRange("A1").setDataValidation(rule).activate();

}

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

//ADD EDIT & REMOVE EMPLOYEE HTML 
function addEmp() {
  var page = HtmlService.createTemplateFromFile("AddEmp").evaluate();
  page.setTitle("💼 Add Employee")
  ui.showSidebar(page)
}
function editEmp() {
  var page = HtmlService.createHtmlOutputFromFile("EditEmp")
    .setTitle("✨ Edit Employee");
  ui.showSidebar(page)
}
function delEmp() {
  var page = HtmlService.createHtmlOutputFromFile("DelEmp")
    .setTitle("🔥 Delete Employee");
  ui.showSidebar(page)
}

//checks for an id# and job code
// if id# already exist, err to checkId
// if job doesn't exist, eff to checkJob

function addEmpRecord(record) {
  let array_target = ws.getRange("C2");
  let array_formula = array_target.getFormula();
  array_target.clear();
  if (!checkId(record[2]) && !checkJob(record[3])) {
    ws.getRange(ws.getLastRow() + 1, 1, 1, 10).setValues([[
      record[0], record[1], , record[2].toString(), record[3].toUpperCase(), record[4], record[5].toUpperCase(), record[6], record[7].toUpperCase(), record[8]
    ]])
    var html = HtmlService.createHtmlOutput("<h4>Entry Accepted</h4><p>Close sidebar to continue or add another employee.</p>")
      .setWidth(300)
      .setHeight(125);
    ui.showModalDialog(html, "Success!")

  }
  else if (checkId(record[2].toString())) {
    var html = HtmlService.createHtmlOutput("<h4>User id# already exists</h4><p>Please choose different id#</p>")
      .setWidth(200)
      .setHeight(75);
    ui.showModalDialog(html, "Warning!")
  }
  else if (checkJob(record[3])) {
    var html = HtmlService.createHtmlOutput("<h4>Job Code does not exitst</h4><p>Please choose a valid job code.<br>*Case sensitive</p>")
      .setWidth(200)
      .setHeight(75);
    ui.showModalDialog(html, "Warning!")
  }
  else {
    var html = HtmlService.createHtmlOutput("<h4>Unspecified error</h4><p>Please contact support.</p>")
      .setWidth(200)
      .setHeight(75);
    ui.showModalDialog(html, "Warning!")
  }
  array_target.setValue(array_formula)
}

// checks if an id# already exists
function checkId(num) {
  var result = false;
  var current_ids = ws.getRange(2, 4, ws.getLastRow(), 1).getValues();
  for (id = 0; id < current_ids.length; id++) {
    if (current_ids[id].toString() == num.toString()) {
      result = true
    }
  }
  return result
}

// checks if a job code exists
function checkJob(job) {
  var result = false;
  var current_jobs = setup.getRange(2, 3, setup.getLastRow(), 1).getValues();
  for (job = 0; job < current_jobs.length; job++) {
    if (current_jobs[job] == job) {
      result = true
    }
  }
  return result
}

function editEmp() {
  Logger.log("editting an employee")
}

function delEmp() {
  Logger.log("deleting an employee")
}

我想利用include(“page”)函数来保持样式,脚本和html的分离性和可读性。我在这里错过了什么?

我认为这与数据的加载和评估方式有关,但看不到如何应用修复程序。

当我将代码拆分为 scriplet include 函数时,我研究了使用 window.onload 事件处理程序来重新评估代码,但我不知道这是否是表单的最佳方法。

javascript google-chrome google-apps-script google-sheets dom

评论

0赞 Cooper 4/4/2023
函数是你写的吗?include()function include(filename) { return HtmlService.createHtmlOutputFromFile(filename).getContent(); }
0赞 J_da_grey 4/4/2023
从服务器端截取代码:function include(page) { return HtmlService.createHtmlOutputFromFile(page).getContent(); }
0赞 Tanaike 4/4/2023
你能提供你的整个剧本吗?
0赞 J_da_grey 4/4/2023
@Tanaike 将引用的服务器脚本添加到帖子中。我认为这可以让你得到你想要的东西。脚本的唯一其他部分处理设置 UI。
0赞 Tanaike 4/4/2023
感谢您的回复。我想确认您更新的问题。显示脚本是当前脚本的整个脚本。我的理解正确吗?

答:

0赞 Tanaike 4/4/2023 #1

我相信你的目标如下。

  • 您想要删除当前的问题。Uncaught TypeError: Cannot read properties of null (reading 'value')

当我看到你的脚本时,在顶部使用了 HTML。在本例中,为 .但是,在您的 Javascript 中,使用了。<div><input id=" fName" type="text" /> First Name</div>id" fName"let fname = document.getElementById("fName").value

我认为您当前问题的原因就是由于这个。因此,请修改如下。Uncaught TypeError: Cannot read properties of null (reading 'value')

从:

<div><input id=" fName" type="text" /> First Name</div>

自:

<div><input id="fName" type="text" /> First Name</div>