0

0

将HTML动态表格多行数据保存到Google Sheet的教程

心靈之曲

心靈之曲

发布时间:2025-12-01 14:51:01

|

716人浏览过

|

来源于php中文网

原创

将HTML动态表格多行数据保存到Google Sheet的教程

本教程旨在解决html表单动态添加多行数据时,google apps script web app仅保存第一行数据的问题。核心解决方案是利用`e.parameters`(复数)获取所有同名输入字段的值数组,并通过修改apps script的`dopost`函数,将这些数据结构化为多行,一次性写入google sheet,从而实现多行数据的完整保存。

在构建交互式Web应用时,我们经常需要将用户在HTML表单中输入的数据保存到后端存储,例如Google Sheet。当HTML表单包含可动态添加的表格行时,一个常见的问题是:尽管用户输入了多行数据,但通过Google Apps Script部署的Web App却只能保存第一行数据。本文将深入探讨这一问题的原因,并提供详细的解决方案。

问题分析:e.parameter与多行数据

在使用Google Apps Script处理POST请求时,e.parameter对象通常用于访问表单提交的单个参数。然而,当HTML表单中存在多个具有相同name属性的输入字段(例如,一个动态表格中的多行Email输入),e.parameter[name]只会返回第一个匹配字段的值。这就是导致只有第一行数据被保存的根本原因。

为了正确处理这种情况,我们需要使用e.parameters(注意是复数),它会返回一个对象,其中每个键对应一个表单字段的name属性,而其值则是一个包含所有同名输入字段值的数组。

解决方案:修改Google Apps Script doPost 函数

假设我们的HTML表单有一个动态表格,包含“Email”和“Name”两列,并且我们希望将这些数据连同提交日期一起保存到Google Sheet。

立即学习前端免费学习笔记(深入)”;

原始 doPost 函数(仅处理单行数据)

// Updated for 2021 and ES6 standards

const sheetName = 'Sheet1'
const scriptProp = PropertiesService.getScriptProperties()

function initialSetup () {
  const activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet()
  scriptProp.setProperty('key', activeSpreadsheet.getId())
}

function doPost (e) {
  const lock = LockService.getScriptLock()
  lock.tryLock(10000)

  try {
    const doc = SpreadsheetApp.openById(scriptProp.getProperty('key'))
    const sheet = doc.getSheetByName(sheetName)

    const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]
    const nextRow = sheet.getLastRow() + 1

    // 这一行是问题的根源,e.parameter[header] 仅获取第一个值
    const newRow = headers.map(function(header) {
      return header === 'Date' ? new Date() : e.parameter[header]
    })

    sheet.getRange(nextRow, 1, 1, newRow.length).setValues([newRow])

    return ContentService
      .createTextOutput(JSON.stringify({ 'result': 'success', 'row': nextRow }))
      .setMimeType(ContentService.MimeType.JSON)
  }

  catch (e) {
    return ContentService
      .createTextOutput(JSON.stringify({ 'result': 'error', 'error': e }))
      .setMimeType(ContentService.MimeType.JSON)
  }

  finally {
    lock.releaseLock()
  }
}

在上述代码中,e.parameter[header]只会获取到第一个“Email”和“Name”字段的值,导致只有一行数据被处理。

修正后的 doPost 函数(处理多行固定列数据)

为了处理多行数据,我们需要修改newRow的构建方式,利用e.parameters获取所有值,并将其重组为适合setValues方法的多维数组。假设Google Sheet的表头顺序为“Date”、“Email”、“Name”。

PHP的使用技巧集
PHP的使用技巧集

PHP 独特的语法混合了 C、Java、Perl 以及 PHP 自创新的语法。它可以比 CGI或者Perl更快速的执行动态网页。用PHP做出的动态页面与其他的编程语言相比,PHP是将程序嵌入到HTML文档中去执行,执行效率比完全生成HTML标记的CGI要高许多。下面介绍了十个PHP高级应用技巧。 1, 使用 ip2long() 和 long2ip() 函数来把 IP 地址转化成整型存储到数据库里

下载
function doPost (e) {
  const lock = LockService.getScriptLock()
  lock.tryLock(10000)

  try {
    const doc = SpreadsheetApp.openById(scriptProp.getProperty('key'))
    const sheet = doc.getSheetByName(sheetName)

    const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]
    const nextRow = sheet.getLastRow() + 1

    // 使用 e.parameters 获取所有同名字段的值数组
    // temp 将是一个数组,例如:[new Date(), ['email1', 'email2'], ['name1', 'name2']]
    const temp = headers.map(header => header === 'Date' ? new Date() : e.parameters[header]);

    // 重构数据为多行多列的数组,适合 setValues
    // 假设 Email 是 temp[1],Name 是 temp[2]
    const newRows = temp[1].map((emailValue, i) => {
      // 每一行的数据结构:[日期, 邮箱, 姓名]
      return [temp[0], emailValue, temp[2][i]];
    });

    // 使用 newRows.length 写入多行数据
    sheet.getRange(nextRow, 1, newRows.length, newRows[0].length).setValues(newRows);

    return ContentService
      .createTextOutput(JSON.stringify({ 'result': 'success', 'row': nextRow }))
      .setMimeType(ContentService.MimeType.JSON)
  }

  catch (e) {
    return ContentService
      .createTextOutput(JSON.stringify({ 'result': 'error', 'error': e }))
      .setMimeType(ContentService.MimeType.JSON)
  }

  finally {
    lock.releaseLock()
  }
}

代码解析:

  1. const temp = headers.map(header => header === 'Date' ? new Date() : e.parameters[header]);
    • 这行代码根据Google Sheet的headers,为每个表头收集对应的数据。
    • 如果表头是“Date”,则生成当前的Date对象。
    • 否则,从e.parameters中获取对应表头(即HTML输入字段的name)的所有值,这将是一个数组。
    • 例如,如果headers是['Date', 'Email', 'Name'],temp可能会是[Date_object, ['email1', 'email2'], ['name1', 'name2']]。
  2. const newRows = temp[1].map((emailValue, i) => { ... });
    • 我们以temp数组中第一个包含多行数据的字段(在本例中是Email,对应temp[1])作为迭代基准。
    • map函数会遍历Email数组的每个元素emailValue及其索引i。
    • 在每次迭代中,我们构建一个代表Google Sheet中一行的数组:[temp[0], emailValue, temp[2][i]]。
      • temp[0]是日期对象(所有行共享)。
      • emailValue是当前行的邮箱
      • temp[2][i]是当前行对应的姓名(通过索引i从Name数组temp[2]中获取)。
    • 最终newRows将是一个二维数组,例如[[Date_object, 'email1', 'name1'], [Date_object, 'email2', 'name2']]。
  3. sheet.getRange(nextRow, 1, newRows.length, newRows[0].length).setValues(newRows);
    • setValues方法被调用,它现在接收一个二维数组,其中newRows.length指定要写入的行数,newRows[0].length指定要写入的列数。这样就能一次性写入所有行数据。

修正后的 doPost 函数(处理多行动态列数据)

如果您的HTML表单和Google Sheet可能包含除了“Date”、“Email”、“Name”之外的更多动态列,并且您希望这些列也能被正确捕获,可以进一步优化doPost函数。

function doPost (e) {
  const lock = LockService.getScriptLock()
  lock.tryLock(10000)

  try {
    const doc = SpreadsheetApp.openById(scriptProp.getProperty('key'))
    const sheet = doc.getSheetByName(sheetName)

    const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]
    const nextRow = sheet.getLastRow() + 1

    const temp = headers.map(header => header === 'Date' ? new Date() : e.parameters[header]);

    // 动态处理更多列
    // 假设 Email 仍然是 temp[1]
    const newRows = temp[1].map((emailValue, i) => {
      // 构建当前行的基础数据:[日期, 邮箱]
      const rowData = [temp[0], emailValue];

      // 动态添加后续列的数据
      // temp.slice(2) 获取 temp 数组中索引为 2 及以后的元素(即除 Date 和 Email 之外的所有列的数据数组)
      // map(f => f[i]) 从每个列的数据数组中取出当前行 (i) 的值
      // ... 展开运算符将这些值添加到 rowData 中
      return [...rowData, ...temp.slice(2).map(f => f[i])];
    });

    sheet.getRange(nextRow, 1, newRows.length, newRows[0].length).setValues(newRows);

    return ContentService
      .createTextOutput(JSON.stringify({ 'result': 'success', 'row': nextRow }))
      .setMimeType(ContentService.MimeType.JSON)
  }

  catch (e) {
    return ContentService
      .createTextOutput(JSON.stringify({ 'result': 'error', 'error': e }))
      .setMimeType(ContentService.MimeType.JSON)
  }

  finally {
    lock.releaseLock()
  }
}

代码解析:

  • const rowData = [temp[0], emailValue];:首先构建包含日期和Email的基础行数据。
  • ...temp.slice(2).map(f => f[i]):
    • temp.slice(2):从temp数组中截取从第三个元素(索引为2)开始的所有元素。这些元素应该都是对应其他列的数据数组(例如['name1', 'name2'], ['col3_row1', 'col3_row2'])。
    • .map(f => f[i]):遍历这些数据数组f,并取出每个数组中索引为i的元素。这样就得到了当前行所有额外列的值。
    • ...:展开运算符将这些值添加到rowData数组中,形成完整的当前行数据。

重要提示:

  • 此修改假设您的HTML表单和Google Sheet的表头已同步更新以包含所有新增列。
  • Google Sheet的表头行必须与HTML表单的name属性保持一致,才能正确映射数据。

示例 HTML 结构

为了让e.parameters能够收集到多行数据,HTML中的输入字段需要具有相同的name属性。例如:

<form method="POST" action="YOUR_WEB_APP_URL">
  <table class="proposedWork" width="100%" style="margin-top:20px">
    <thead>
      <th>Email</th>
      <th>Name</th>
      <th class="docEdit trAdd">+</th>
    </thead>
    <tbody>
      <tr>
        <td contenteditable="true">
          <input name="Email" type="email" placeholder="Email" required>
        </td>
        <td contenteditable="true">
          <input name="Name" type="name" placeholder="Name" required>
        </td>
        <td class="docEdit tdDelete">X</td>
      </tr>
      <!-- 动态添加的行也会有相同的 name="Email" 和 name="Name" -->
      <tr>
        <td contenteditable="true">
          <input name="Email" type="email" placeholder="Email" required>
        </td>
        <td contenteditable="true">
          <input name="Name" type="name" placeholder="Name" required>
        </td>
        <td class="docEdit tdDelete">X</td>
      </tr>
    </tbody>
  </table>
  <button type="submit">Send</button>
</form>

上述HTML代码中的关键在于,每一行中的Email和Name输入框都使用相同的name属性(name="Email"和name="Name")。当表单提交时,e.parameters会收集所有这些同名输入框的值,并将其作为数组提供。

注意事项与部署

  1. Google Sheet 表头一致性: 确保您的Google Sheet第一行表头与HTML表单中input元素的name属性严格匹配。
  2. Web App 重新部署: 每当您修改了Google Apps Script代码后,必须将Web App部署为新版本。否则,您的更改将不会生效。
    • 在Apps Script编辑器中,点击“部署” -> “管理部署”。
    • 选择您的Web App部署,点击铅笔图标编辑。
    • 在“版本”下拉菜单中选择“新建版本”,然后点击“部署”。

总结

通过将Google Apps Script中的e.parameter替换为e.parameters,并相应地重构数据处理逻辑,我们可以有效地解决HTML动态表格多行数据无法完整保存到Google Sheet的问题。理解e.parameters的工作原理以及如何将其转换为适合setValues方法的多维数组是实现此功能的关键。务必记住在每次代码修改后重新部署Web App以应用更改。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1561

2023.10.24

Go语言中的运算符有哪些
Go语言中的运算符有哪些

Go语言中的运算符有:1、加法运算符;2、减法运算符;3、乘法运算符;4、除法运算符;5、取余运算符;6、比较运算符;7、位运算符;8、按位与运算符;9、按位或运算符;10、按位异或运算符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

241

2024.02.23

php三元运算符用法
php三元运算符用法

本专题整合了php三元运算符相关教程,阅读专题下面的文章了解更多详细内容。

128

2025.10.17

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

558

2023.09.20

treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

548

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

27

2025.12.22

深入理解算法:高效算法与数据结构专题
深入理解算法:高效算法与数据结构专题

本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

44

2026.01.06

length函数用法
length函数用法

length函数用于返回指定字符串的字符数或字节数。可以用于计算字符串的长度,以便在查询和处理字符串数据时进行操作和判断。 需要注意的是length函数计算的是字符串的字符数,而不是字节数。对于多字节字符集,一个字符可能由多个字节组成。因此,length函数在计算字符串长度时会将多字节字符作为一个字符来计算。更多关于length函数的用法,大家可以阅读本专题下面的文章。

953

2023.09.19

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

4

2026.03.05

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
ECMAScript6 / ES6---十天技能课堂
ECMAScript6 / ES6---十天技能课堂

共25课时 | 2.1万人学习

HTML5/CSS3/JavaScript/ES6入门课程
HTML5/CSS3/JavaScript/ES6入门课程

共102课时 | 7.3万人学习

HTML+CSS基础与实战
HTML+CSS基础与实战

共132课时 | 12.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号