├── .codeclimate.yml ├── HtmlFormApp.js ├── LICENCE ├── README.md ├── appsscript.json └── images └── fig1.png /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | duplication: 4 | enabled: true 5 | config: 6 | languages: 7 | - ruby 8 | - javascript 9 | - python 10 | - php 11 | eslint: 12 | enabled: true 13 | fixme: 14 | enabled: true 15 | ratings: 16 | paths: 17 | - "**.inc" 18 | - "**.js" 19 | - "**.jsx" 20 | - "**.module" 21 | - "**.php" 22 | - "**.py" 23 | - "**.rb" 24 | exclude_paths: 25 | - "tests/" 26 | - "spec/" 27 | - "**/vendor/" 28 | -------------------------------------------------------------------------------- /HtmlFormApp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * GitHub https://github.com/tanaikech/HtmlFormApp
3 | * Library name 4 | * @type {string} 5 | * @const {string} 6 | * @readonly 7 | */ 8 | var appName = "HtmlFormApp"; 9 | 10 | /** 11 | * Append HTML form data to Spreadsheet.
12 | * @param {object} object Object including formData returned from HtmlFormObjectParserForGoogleAppsScript_js. 13 | * @param {number} rowNumber The row number to edit (1-based index). 14 | * @return {object} object Object including Class Spreadsheet, Class Sheet, Class Range of the row put the values, values 15 | */ 16 | function appendFormData(object, row = null) { 17 | return new HtmlFormApp(object).appendFormData(row); 18 | } 19 | ; 20 | (function (r) { 21 | var HtmlFormApp; 22 | HtmlFormApp = (function () { 23 | var checkHeader, checkObject, convObj, setHyperLinks; 24 | 25 | class HtmlFormApp { 26 | constructor(obj_) { 27 | this.name = appName; 28 | this.obj = {}; 29 | checkObject.call(this, obj_); 30 | } 31 | 32 | appendFormData(row_) { 33 | var header, hyperlinks, keyConvertObj, range, values; 34 | header = checkHeader.call(this); 35 | keyConvertObj = Object.entries(this.formObject).reduce((o, [k, v]) => { 36 | o[k.trim().toLocaleLowerCase()] = v; 37 | return o; 38 | }, {}); 39 | hyperlinks = false; 40 | values = [ 41 | header.map((hv) => { 42 | var h; 43 | if (this.headerConversion && (!this.ignoreHeader || this.ignoreHeader === false)) { 44 | hv = this.headerConversion[hv] || hv; 45 | } 46 | h = hv.trim().toLocaleLowerCase(); 47 | if (h === "date") { 48 | return new Date(); 49 | } else if (keyConvertObj[h] && keyConvertObj[h].some((e) => { 50 | return e.files; 51 | })) { 52 | return (keyConvertObj[h].reduce((ar, 53 | e) => { 54 | if (e.files && e.files.length > 0) { 55 | hyperlinks = true; 56 | e.files.forEach(({ bytes, 57 | mimeType, 58 | filename }) => { 59 | return ar.push(this.obj.folder.createFile(Utilities.newBlob(bytes, 60 | mimeType, 61 | filename)).getUrl()); 62 | }); 63 | } else { 64 | ar.push(""); 65 | } 66 | return ar; 67 | }, 68 | [])).join(this.delimiter); 69 | } else if (keyConvertObj[h] && (keyConvertObj[h][0].type === "checkbox" || keyConvertObj[h][0].type === "radio")) { 70 | if (this.choiceFormat && this.choiceFormat === true) { 71 | return (keyConvertObj[h].map(({ checked, 72 | value }) => { 73 | return `${value}(${checked === true ? "checked" : "unchecked"})`; 74 | })).join(this.delimiter); 75 | } 76 | return (keyConvertObj[h].reduce((arr, 77 | { checked, 78 | value }) => { 79 | if (checked === true) { 80 | arr.push(value); 81 | } 82 | return arr; 83 | }, 84 | [])).join(this.delimiter); 85 | } 86 | if (keyConvertObj[h]) { 87 | if (keyConvertObj[h].length === 1) { 88 | return keyConvertObj[h][0].value; 89 | } else { 90 | return (keyConvertObj[h].map(({ value }) => { 91 | return value; 92 | })).join(this.delimiter); 93 | } 94 | } 95 | return ""; 96 | }) 97 | ]; 98 | if (this.lastRow === 0 && (!this.ignoreHeader || this.ignoreHeader === false)) { 99 | values.unshift(header); 100 | } 101 | range = this.sheet.getRange(row_ || this.lastRow + 1, 1, values.length, values[0].length); 102 | range.setValues(values); 103 | if (hyperlinks === true) { 104 | setHyperLinks.call(this, range, values); 105 | } 106 | return { 107 | spreadsheet: this.obj.spreadsheet, 108 | sheet: this.sheet, 109 | range: range, 110 | values: values 111 | }; 112 | } 113 | 114 | }; 115 | 116 | HtmlFormApp.name = appName; 117 | 118 | checkHeader = function () { 119 | var header, hedFromSheet, oo; 120 | header = ["Date", ...this.orderOfFormObject]; 121 | if (this.lastRow > 0 && (!this.ignoreHeader || this.ignoreHeader === false)) { 122 | hedFromSheet = this.sheet.getRange(1, 1, 1, this.sheet.getLastColumn()).getValues()[0]; 123 | oo = this.orderOfFormObject.reduce((o, e) => { 124 | o[e.toLocaleLowerCase()] = true; 125 | return o; 126 | }, {}); 127 | if (hedFromSheet.some((e) => { 128 | return oo[e.toLocaleLowerCase()]; 129 | })) { 130 | header = hedFromSheet; 131 | } 132 | } 133 | return header; 134 | }; 135 | 136 | checkObject = function (obj_) { 137 | var _; 138 | if (!obj_) { 139 | throw new Error("Object for using this library is not given."); 140 | } 141 | if (!obj_.hasOwnProperty("formData")) { 142 | throw new Error("'formData' is not included in your object."); 143 | } 144 | this.obj = obj_; 145 | if (this.obj.formData.hasOwnProperty("parameters") && this.obj.formData.hasOwnProperty("parameter")) { 146 | if (this.obj.formData.hasOwnProperty("postData") && this.obj.formData.postData.hasOwnProperty("contents") && this.obj.formData.postData.hasOwnProperty("name") && this.obj.formData.postData.name === "postData") { 147 | if (this.obj.formData.postData.contents === "") { 148 | throw new Error(`Request body is not found.`); 149 | } 150 | try { 151 | this.obj.formData = JSON.parse(this.obj.formData.postData.contents); 152 | } catch (error) { 153 | _ = error; 154 | this.obj.formData = convObj.call(this, this.obj.formData.parameters); 155 | } 156 | } else if (!this.obj.formData.parameter.hasOwnProperty("formData")) { 157 | this.obj.formData = Object.fromEntries(Object.entries(this.obj.formData.parameters).map(([k, v]) => { 158 | return [ 159 | k, 160 | v.map((e) => { 161 | return { 162 | value: e 163 | }; 164 | }) 165 | ]; 166 | })); 167 | } else { 168 | if (!this.obj.formData.parameter.hasOwnProperty("formData") || this.obj.formData.parameter.hasOwnProperty("formData") === "") { 169 | throw new Error(`Query parameter is not found.`); 170 | } 171 | this.obj.formData = JSON.parse(this.obj.formData.parameter.formData); 172 | } 173 | } 174 | this.obj.folder = this.obj.folderId && this.obj.folderId !== "" ? DriveApp.getFolderById(this.obj.folderId) : DriveApp.getRootFolder(); 175 | if (!Object.values(this.obj.formData).every((e) => { 176 | return Array.isArray(e); 177 | })) { 178 | this.obj.formData = convObj.call(this, this.obj.formData); 179 | } 180 | if (this.obj.spreadsheetId && this.obj.spreadsheetId !== "") { 181 | this.obj.spreadsheet = SpreadsheetApp.openById(this.obj.spreadsheetId); 182 | this.obj.new = false; 183 | } else { 184 | this.obj.spreadsheet = SpreadsheetApp.create("spreadsheetCreatedByHtmlFormApp"); 185 | this.obj.new = true; 186 | } 187 | if (!this.obj.spreadsheetId) { 188 | this.obj.spreadsheetId = this.obj.spreadsheet.getId(); 189 | } 190 | if (this.obj.sheetName && this.obj.sheetName !== "") { 191 | this.obj.sheet = this.obj.spreadsheet.getSheetByName(this.obj.sheetName); 192 | if (!this.obj.sheet) { 193 | throw new Error(`Sheet of ${this.obj.sheetName} is not found.`); 194 | } 195 | } 196 | if (this.obj.sheetId && this.obj.sheetId !== "") { 197 | this.obj.sheet = this.obj.spreadsheet.getSheets().find((e) => { 198 | return e.getSheetId() === this.obj.sheetId; 199 | }); 200 | if (!this.obj.sheet) { 201 | throw new Error(`Sheet of ${this.obj.sheetId} is not found.`); 202 | } 203 | } 204 | if (!this.obj.sheetName && !this.obj.sheetId) { 205 | this.obj.sheet = this.obj.spreadsheet.getSheets()[0]; 206 | } 207 | this.sheet = this.obj.sheet; 208 | this.formObject = this.obj.formData; 209 | this.orderOfFormObject = this.formObject.orderOfFormObject || Object.keys(this.formObject); 210 | this.headerConversion = this.obj.headerConversion; 211 | this.ignoreHeader = this.obj.ignoreHeader; 212 | this.lastRow = this.sheet.getLastRow(); 213 | this.choiceFormat = this.obj.choiceFormat; 214 | this.delimiter = this.obj.delimiterOfMultipleAnswers || ","; 215 | this.valueAsRaw = this.obj.valueAsRaw || false; 216 | if (!this.valueAsRaw) { 217 | return Object.entries(this.formObject).forEach(([k, v]) => { 218 | var t; 219 | if (k !== "orderOfFormObject" && Array.isArray(v) && v[0].hasOwnProperty("type")) { 220 | t = v[0].type.toLowerCase(); 221 | if (t === "number") { 222 | v.forEach((e, i) => { 223 | return this.formObject[k][i].value = Number(e.value); 224 | }); 225 | } 226 | if (t === "date") { 227 | v.forEach((e, i) => { 228 | return this.formObject[k][i].value = new Date(e.value + "T00:00:00"); 229 | }); 230 | } 231 | if (t === "datetime-local") { 232 | return v.forEach((e, i) => { 233 | return this.formObject[k][i].value = new Date(e.value); 234 | }); 235 | } 236 | } 237 | }); 238 | } 239 | }; 240 | 241 | convObj = function (obj_) { 242 | return Object.fromEntries(Object.entries(obj_).map(([k, v]) => { 243 | if (typeof v === "object" && v.hasOwnProperty("contents") && v.hasOwnProperty("length") && v.hasOwnProperty("name") && v.hasOwnProperty("type")) { 244 | v = this.obj.folder.createFile(v).getUrl(); 245 | } 246 | return [ 247 | k, 248 | Array.isArray(v) ? v.map((e) => { 249 | return { 250 | value: e 251 | }; 252 | }) : [ 253 | { 254 | value: v 255 | } 256 | ] 257 | ]; 258 | })); 259 | }; 260 | 261 | setHyperLinks = function (range, values) { 262 | var re; 263 | re = new RegExp(this.delimiter, "g"); 264 | return values.forEach((r, i) => { 265 | return r.forEach((c, j) => { 266 | var indexes, offset, rich, t; 267 | if (c && /https?:\/\//i.test(c)) { 268 | t = [...c.matchAll(re)]; 269 | rich = SpreadsheetApp.newRichTextValue().setText(c); 270 | if (t.length === 0) { 271 | rich.setLinkUrl(c); 272 | } else if (c.includes(this.delimiter)) { 273 | offset = 0; 274 | t.forEach((e) => { 275 | var indexes; 276 | indexes = [offset, e.index]; 277 | rich.setLinkUrl(...indexes, c.slice(...indexes)); 278 | return offset = e.index + 1; 279 | }); 280 | indexes = [t.pop().index + 1, c.length]; 281 | rich.setLinkUrl(...indexes, c.slice(...indexes)); 282 | } 283 | return range.offset(i, j).setRichTextValue(rich.build()); 284 | } 285 | }); 286 | }); 287 | }; 288 | 289 | return HtmlFormApp; 290 | 291 | }).call(this); 292 | return r.HtmlFormApp = HtmlFormApp; 293 | })(this); 294 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2022 Kanshi TANAIKE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HtmlFormApp 2 | 3 | 4 | [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENCE) 5 | 6 | 7 | 8 | # Overview 9 | 10 | ![](images/fig1.png) 11 | 12 | This is a Google Apps Script library for parsing the form object from HTML form and appending the submitted values to the Spreadsheet. 13 | 14 | 15 | 16 | ## Description 17 | 18 | There is Google Form in the Google service. Google Form can parse the submitted data and put it in the Spreadsheet. But when we want to use the custom form, it is required to use the HTML form on Web Apps, dialog, and sidebar. In this case, it is required to prepare Javascript and Google Apps Script for parsing the form object from the HTML form and appending the parsed values to Spreadsheet. Recently, a bug of the built-in parser from the Javascript side to the Google Apps Script side for parsing the form object from the HTML form had been removed. [Ref](https://tanaikech.github.io/2021/12/09/fixed-google-apps-script-web-app-html-form-file-input-fields-not-in-blob-compatible-format/) But, in the current stage, this bug is removed for only Web Apps. Unfortunately, for the dialog and sidebar, this bug has never been removed. And also, unfortunately, the built-in parser from the Javascript side to the Google Apps Script side cannot be used for the multiple files of the input tag. And, this cannot be used except for `google.script.run`. For example, when the HTML form including the files is submitted using "action" of the form tag, the file content is not included. And then, when the form object is retrieved, it is required to parse the object and put it in the Spreadsheet. From these situations, I thought that when this process can be run using the libraries, that might be useful for users. So I created this. 19 | 20 | This Google Apps Script library uses a Javascript library of [HtmlFormObjectParserForGoogleAppsScript_js](https://github.com/tanaikech/HtmlFormObjectParserForGoogleAppsScript_js). The form object from the HTML form is parsed using the Javascript library and sent to Google Apps Script side, and the object from Javascript is parsed and put to Spreadsheet using this Google Apps Script library. 21 | 22 | ## `medium.com` 23 | 24 | [Easily Implementing HTML Form with Google Spreadsheet as Database using Google Apps Script](https://medium.com/google-cloud/easily-implementing-html-form-with-google-spreadsheet-as-database-using-google-apps-script-66472ab7bf6c) 25 | 26 | # Library's project key 27 | 28 | ``` 29 | 1uLJrVXGaI-ceHFl_VC1U5jcynKpR2qnNG2tNPd03QJZw1jCcKw2_Oiwh 30 | ``` 31 | 32 | # Methods 33 | 34 | | Methods | Description | 35 | | :---------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 36 | | [appendFormData(object, row)](#appendFormData) | Parsing the form object parsed by [HtmlFormObjectParserForGoogleAppsScript_js](https://github.com/tanaikech/HtmlFormObjectParserForGoogleAppsScript_js) and append the value to Spreadsheet. | 37 | 38 | ## About scopes 39 | 40 | This library use the scopes of `https://www.googleapis.com/auth/drive` and `https://www.googleapis.com/auth/spreadsheets`. 41 | 42 | 43 | 44 | # Usage: 45 | 46 | ## 1. Install library 47 | 48 | In order to use this library, please install this library as follows. 49 | 50 | 1. Create a GAS project. 51 | 52 | - You can use this library for the GAS project of both the standalone type and the container-bound script type. 53 | 54 | 1. [Install this library](https://developers.google.com/apps-script/guides/libraries). 55 | 56 | - Library's project key is **`1uLJrVXGaI-ceHFl_VC1U5jcynKpR2qnNG2tNPd03QJZw1jCcKw2_Oiwh`**. 57 | 58 | ## Methods 59 | 60 | 61 | 62 | ### appendFormData 63 | 64 | This method parses the form object parsed by [HtmlFormObjectParserForGoogleAppsScript_js](https://github.com/tanaikech/HtmlFormObjectParserForGoogleAppsScript_js) and appends the value to Spreadsheet. 65 | 66 | ```javascript 67 | // These are all options. 68 | const obj = { 69 | formData: formData, 70 | spreadsheetId: "###", 71 | sheetName: "###", 72 | sheetId: "###", 73 | folderId: "###", 74 | headerConversion: {"header value of Spreadsheet": "name of HTML input tag",,,}, 75 | ignoreHeader: true, 76 | choiceFormat: true, 77 | delimiterOfMultipleAnswers: "\n", 78 | valueAsRaw: true 79 | }; 80 | const res = HtmlFormApp.appendFormData(obj); 81 | console.log(res) 82 | ``` 83 | 84 | #### Input object 85 | 86 | - **formData**: Form object from HtmlFormObjectParserForGoogleAppsScript_js 87 | 88 | - **spreadsheetId**: Spreadsheet ID you want to put the values. 89 | 90 | - When `spreadsheetId` is not used, the values are put to the 1st sheet of the created new Spreadsheet. 91 | 92 | - **sheetName**: Sheet name you want to put the values. Default is 1st sheet. When you don't use this, the value is put on the 1st sheet. 93 | 94 | - **sheetId**: Sheet ID you want to put the values. Default is 1st sheet. When you don't use this, the value is put on the 1st sheet. 95 | 96 | - **folderId**: Folder ID of the folder putting the submitted file. Default is "root". When you don't use this, the submitted files are put to the root folder. 97 | 98 | - **headerConversion**: When the header values in the sheet are different from each name of the HTML form, you can put the values to the sheet using this object. 99 | 100 | - For example, when the names of input tags in HTML form are "sample1", "sample2", "sample3", and when the header row is "test1", "test2", "test3", you can set `headerConversion` as `headerConversion: {"test1": "sample1", "test2": "sample2", "test3": "sample3"}`. 101 | 102 | - **ignoreHeader**: When this value is true, the submitted values are put to the sheet without using the header row. Default is false. 103 | 104 | - **choiceFormat**: When the HTML form includes the multiple-choice like the checkboxes and radio button, when this value is true, values both true and false are put to the sheet like `value1(checked)\nvalue2(unchecked)`. Default is false. When this value is false, only values when it is true are put on the sheet. 105 | 106 | - **delimiterOfMultipleAnswers**: When the multiple answers are included in an input tag, the values are put to the sheet using this delimiter. Default is ",". 107 | 108 | - **valueAsRaw**: When this value is `true`, the raw values retrieved from HTML form are used. Default is `false`. When this value is not used to `false`, for example, the date is converted to the date object. 109 | 110 | - About 2nd argument of `appendFormData(object, row)`, this is from [this suggestion](https://github.com/tanaikech/HtmlFormApp/issues/1). When `row` is used, the value is put into the specific row of the Spreadsheet. In this case, please set the value of `row` more than 1. 111 | - In this case, the submitted row can be forcefully put into the specific row of Google Spreadsheet. So, when you run `appendFormData(object, row)` by the constant value of `row`, the submitted row is put into the same row. Please be careful about this. 112 | 113 | #### Output object 114 | 115 | This method of `appendFormData` returns the following object. 116 | 117 | ```json 118 | { 119 | "spreadsheet": "Class Spreadsheet", 120 | "sheet": "Class Sheet", 121 | "range": "Class Range", 122 | "values": "values put to the sheet" 123 | } 124 | ``` 125 | 126 | By using this, you can retrieve the range of the submitted row like `res.range.getRow()`. 127 | 128 | #### Simple sample script 129 | 130 | For example, when you want to use this library as a simple mode, you can use the following script. 131 | 132 | ```javascript 133 | const obj = { 134 | formData: formData, 135 | spreadsheetId: "###", 136 | }; 137 | const res = HtmlFormApp.appendFormData(obj); 138 | console.log(res); 139 | ``` 140 | 141 | In this script, the form data is append to the 1st sheet of "spreadsheetId". 142 | 143 | # Sample scripts 144 | 145 | In the current stage, in order to submit the values using an HTML form, it is considered the following situations. Here, I would like to introduce the sample scripts for each situation using this library. 146 | 147 | "Multiple files", "Single file" and "No files" indicate whether the multiple files (using `multiple` in the input tag.) can be uploaded using the HTML form. Even when "Single file" is indicated, when you put the multiple input tags (`type="file"`) without using `multiple`, you can upload the multiple files. 148 | 149 | Of course, the values from the inputted texts, checkboxes, radio buttons, dropdown lists can be retrieved for all situations. 150 | 151 | | Situations | Multiple files | Single file | Sample scripts | 152 | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------- | :---------- | :------------------ | 153 | | Using HtmlFormObjectParserForGoogleAppsScript_js, the form object is sent from HTML side to Google Apps Script side by `google.script.run` on the dialog, sidebar and Web Apps. | Yes | Yes | [Sample1](#sample1) | 154 | | Using HtmlFormObjectParserForGoogleAppsScript_js, the form object is sent from HTML side to Google Apps Script by `fetch` of Javascript with the POST method on the dialog, sidebar, Web Apps and HTML in other site. | Yes | Yes | [Sample2](#sample2) | 155 | | Using HtmlFormObjectParserForGoogleAppsScript_js, the form object is sent from HTML side to Google Apps Script by `fetch` of Javascript with the GET method on the dialog, sidebar, Web Apps and HTML in other site. | | | [Sample3](#sample3) | 156 | | The form object is sent from HTML side to Google Apps Script by the parser like `google.script.run.myFunction(e.parentNode)` on the Web Apps. | | Yes | [Sample4](#sample4) | 157 | | The form object is sent from HTML side to Google Apps Script by the parser like `google.script.run.myFunction(e.parentNode)` on the dialog and sidebar. | | | [Sample4](#sample4) | 158 | | The form object is sent from HTML side to Google Apps Script by `
` of the form tag on the dialog, sidebar, Web Apps and HTML in other site. | | | [Sample5](#sample5) | 159 | | The form object is sent from HTML side to Google Apps Script by `` of the form tag on the dialog, sidebar, Web Apps and HTML in other site. | | | [Sample6](#sample6) | 160 | 161 | 162 | 163 | ## 1. Sample 1 164 | 165 | Using HtmlFormObjectParserForGoogleAppsScript_js, the form object is sent from HTML side to Google Apps Script side by `google.script.run` on the dialog, sidebar. 166 | 167 | ### HTML and Javascript side: `index.html` 168 | 169 | Please copy and paste this script to the script editor of Spreadsheet as a HTML file. This sample uses a dialog. 170 | 171 | ```html 172 | 173 | Text1:
174 | Text2:
175 | Number:
176 | Date:
177 | File:
178 | CheckBox: 1
179 | CheckBox: 2
180 | Radiobutton: 1
181 | Radiobutton: 2
182 | 188 |
189 | 190 | 191 | 192 | 201 | ``` 202 | 203 | At `ParseFormObjectForGAS` method, the attribution of `name` of each input tag is used as the key of the parsed form object. 204 | 205 | ### Google Apps Script side: `Code.gs` 206 | 207 | Please copy and paste this script to the script editor of Spreadsheet as a script file. And please set your Spreadsheet ID. And, please run `myFunction`. By this, a dialog is opened. When you input the values into the dialog and click the submit button, the values are put on the 1st sheet of the Spreadsheet. 208 | 209 | ```javascript 210 | function myFunction() { 211 | const html = HtmlService.createHtmlOutputFromFile("index"); 212 | SpreadsheetApp.getUi().showDialog(html); 213 | } 214 | 215 | function main(formData) { 216 | const obj = { 217 | formData: formData, 218 | spreadsheetId: "###spreadsheetId###", 219 | ignoreHeader: true, 220 | }; 221 | const res = HtmlFormApp.appendFormData(obj); 222 | console.log(res.range.getRow()); 223 | return "Done"; 224 | } 225 | ``` 226 | 227 | - If you want to put the values using the header row, please set the header row to the Spreadsheet and remove `ignoreHeader: true,`. 228 | 229 | 230 | 231 | ## 2. Sample 2 232 | 233 | Using HtmlFormObjectParserForGoogleAppsScript_js, the form object is sent from HTML side to Google Apps Script side by `google.script.run` on Web Apps. 234 | 235 | ### HTML and Javascript side: `index.html` 236 | 237 | Please create a HTML file including this script. And please set your Web Apps URL. 238 | 239 | ```html 240 |
241 | Text1:
242 | Text2:
243 | Number:
244 | Date:
245 | File:
246 | CheckBox: 1
247 | CheckBox: 2
248 | Radiobutton: 1
249 | Radiobutton: 2
250 | 256 |
257 | 258 | 259 | 260 | 270 | ``` 271 | 272 | - At `ParseFormObjectForGAS` method, the attribution of `name` of each input tag is used as the key of the parsed form object. 273 | 274 | ### Google Apps Script side: `Code.gs` 275 | 276 | ```javascript 277 | function doPost(e) { 278 | const obj = { 279 | formData: e, 280 | spreadsheetId: "###spreadsheetId###", 281 | ignoreHeader: true, 282 | }; 283 | const res = HtmlFormApp.appendFormData(obj); 284 | return ContentService.createTextOutput( 285 | JSON.stringify({ message: "Done", row: res.range.getRow() }) 286 | ); 287 | } 288 | ``` 289 | 290 | 1. Please copy and paste this script to the script editor of Google Apps Script. Of course, you can use both the standalone type and the container-bound script type as the Google Apps Script project. And please set your Spreadsheet ID. 291 | 292 | 2. Please deploy Web Apps as `Execute as: Me` and `Who has access to the app: Anyone`. You can see how to deploy Web Apps at [here](https://developers.google.com/apps-script/guides/web). 293 | 294 | - When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this. 295 | - You can see the detail of this in the report of "[Redeploying Web Apps without Changing URL of Web Apps for new IDE](https://gist.github.com/tanaikech/ebf92d8f427d02d53989d6c3464a9c43)". 296 | 297 | 3. Please open your HTML file using your browser. When you input the values into the dialog and click the submit button, the values are put on the 1st sheet of the Spreadsheet. 298 | 299 | - If you want to put the values using the header row, please set the header row to the Spreadsheet and remove `ignoreHeader: true,`. 300 | 301 | 302 | 303 | ## 3. Sample 3 304 | 305 | Using HtmlFormObjectParserForGoogleAppsScript_js, the form object is sent from HTML side to Google Apps Script by `fetch` of Javascript with the GET method on the dialog, sidebar, Web Apps and HTML in other site. 306 | 307 | **In this sample, the files cannot be uploaded. Please be careful this.** 308 | 309 | ### HTML and Javascript side: `index.html` 310 | 311 | Please create an HTML file including this script. And please set your Web Apps URL. Of course, you can also use this HTML and Javascript on a dialog and sidebar. 312 | 313 | ```html 314 |
315 | Text1:
316 | Text2:
317 | Number:
318 | Date:
319 | 320 | CheckBox: 1
321 | CheckBox: 2
322 | Radiobutton: 1
323 | Radiobutton: 2
324 | 330 |
331 | 332 | 333 | 334 | 344 | ``` 345 | 346 | - At `ParseFormObjectForGAS` method, the attribution of `name` of each input tag is used as the key of the parsed form object. 347 | 348 | - **In this case, the maximum length of the URL is 2 kb. So if you want to include the file, please be careful this.** 349 | 350 | ### Google Apps Script side: `Code.gs` 351 | 352 | ```javascript 353 | function doGet(e) { 354 | const obj = { 355 | formData: e, 356 | spreadsheetId: "###spreadsheetId###", 357 | ignoreHeader: true, 358 | }; 359 | const res = HtmlFormApp.appendFormData(obj); 360 | return ContentService.createTextOutput( 361 | JSON.stringify({ message: "Done", row: res.range.getRow() }) 362 | ); 363 | } 364 | ``` 365 | 366 | 1. Please copy and paste this script to the script editor of Google Apps Script. Of course, you can use both the standalone type and the container-bound script type as the Google Apps Script project. And please set your Spreadsheet ID. 367 | 368 | 2. Please deploy Web Apps as `Execute as: Me` and `Who has access to the app: Anyone`. You can see how to deploy Web Apps at [here](https://developers.google.com/apps-script/guides/web). 369 | 370 | - When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this. 371 | - You can see the detail of this in the report of "[Redeploying Web Apps without Changing URL of Web Apps for new IDE](https://gist.github.com/tanaikech/ebf92d8f427d02d53989d6c3464a9c43)". 372 | 373 | 3. Please open your HTML file using your browser. When you input the values into the dialog and click the submit button, the values are put on the 1st sheet of the Spreadsheet. 374 | 375 | - If you want to put the values using the header row, please set the header row to the Spreadsheet and remove `ignoreHeader: true,`. 376 | 377 | 378 | 379 | ## 4. Sample 4 380 | 381 | The form object is sent from HTML side to Google Apps Script by the parser like `google.script.run.myFunction(e.parentNode)` on the Web Apps. 382 | 383 | **In this sample, only one file can be uploaded. Please be careful this.** 384 | 385 | Also, you can use this with the dialog and sidebar. But in the current stage, in order to parse `e.parentNode` with `google.script.run` on the dialog and sidebar, unfortunately, the file cannot be parsed. So please be careful about this. [Ref](https://issuetracker.google.com/issues/155109626) 386 | 387 | ### HTML and Javascript side: `index.html` 388 | 389 | Please create an HTML file including this script. And please set your Web Apps URL. Of course, you can also use this HTML and Javascript on a dialog and sidebar. 390 | 391 | ```html 392 |
393 | Text1:
394 | Text2:
395 | Number:
396 | Date:
397 | File:
398 | CheckBox: 1
399 | CheckBox: 2
400 | Radiobutton: 1
401 | Radiobutton: 2
402 | 408 |
409 | 410 | 418 | ``` 419 | 420 | - At `ParseFormObjectForGAS` method, the attribution of `name` of each input tag is used as the key of the parsed form object. 421 | 422 | - **In this case, the maximum length of the URL is 2 kb. So if you want to include the file, please be careful this.** 423 | 424 | ### Google Apps Script side: `Code.gs` 425 | 426 | Please copy and paste this script to the script editor of Spreadsheet as a script file. And please set your Spreadsheet ID. And, please run `myFunction`. By this, a dialog is opened. When you input the values into the dialog and click the submit button, the values are put on the 1st sheet of the Spreadsheet. 427 | 428 | ```javascript 429 | function myFunction() { 430 | const html = HtmlService.createHtmlOutputFromFile("index"); 431 | SpreadsheetApp.getUi().showDialog(html); 432 | } 433 | 434 | function main(formData) { 435 | const obj = { 436 | formData: formData, 437 | spreadsheetId: "###spreadsheetId###", 438 | ignoreHeader: true, 439 | }; 440 | const res = HtmlFormApp.appendFormData(obj); 441 | console.log(res.range.getRow()); 442 | return "Done"; 443 | } 444 | ``` 445 | 446 | - If you want to put the values using the header row, please set the header row to the Spreadsheet and remove `ignoreHeader: true,`. 447 | 448 | 449 | 450 | ## 5. Sample 5 451 | 452 | The form object is sent from HTML side to Google Apps Script by `
` of the form tag on the dialog, sidebar, Web Apps and HTML in other site. 453 | 454 | **In this sample, the files cannot be uploaded. Please be careful this.** 455 | 456 | ### HTML and Javascript side: `index.html` 457 | 458 | Please create an HTML file including this script. And please set your Web Apps URL. Of course, you can also use this HTML and Javascript on a dialog and sidebar. 459 | 460 | ```html 461 | 462 | Text1:
463 | Text2:
464 | Number:
465 | Date:
466 | CheckBox: 1
467 | CheckBox: 2
468 | Radiobutton: 1
469 | Radiobutton: 2
470 | 471 |
472 | ``` 473 | 474 | ### Google Apps Script side: `Code.gs` 475 | 476 | ```javascript 477 | function doPost(e) { 478 | const obj = { 479 | formData: e, 480 | spreadsheetId: "###spreadsheetId###", 481 | ignoreHeader: true, 482 | }; 483 | const res = HtmlFormApp.appendFormData(obj); 484 | return ContentService.createTextOutput( 485 | JSON.stringify({ message: "Done", row: res.range.getRow() }) 486 | ); 487 | } 488 | ``` 489 | 490 | 1. Please copy and paste this script to the script editor of Google Apps Script. Of course, you can use both the standalone type and the container-bound script type as the Google Apps Script project. And please set your Spreadsheet ID. 491 | 492 | 2. Please deploy Web Apps as `Execute as: Me` and `Who has access to the app: Anyone`. You can see how to deploy Web Apps at [here](https://developers.google.com/apps-script/guides/web). 493 | 494 | - When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this. 495 | - You can see the detail of this in the report of "[Redeploying Web Apps without Changing URL of Web Apps for new IDE](https://gist.github.com/tanaikech/ebf92d8f427d02d53989d6c3464a9c43)". 496 | 497 | 3. Please open your HTML file using your browser. When you input the values into the dialog and click the submit button, the values are put on the 1st sheet of the Spreadsheet. 498 | 499 | - If you want to put the values using the header row, please set the header row to the Spreadsheet and remove `ignoreHeader: true,`. 500 | 501 | 502 | 503 | ## 6. Sample 6 504 | 505 | The form object is sent from HTML side to Google Apps Script by `
` of the form tag on the dialog, sidebar, Web Apps and HTML in other site. 506 | 507 | **In this sample, the files cannot be uploaded. Please be careful this.** 508 | 509 | ### HTML and Javascript side: `index.html` 510 | 511 | Please create a HTML file including this script. And please set your Web Apps URL. Of course, you can also use this HTML and Javascript on a dialog and sidebar. 512 | 513 | ```html 514 | 519 | Text1:
520 | Text2:
521 | Number:
522 | Date:
523 | CheckBox: 1
524 | CheckBox: 2
525 | Radiobutton: 1
526 | Radiobutton: 2
527 | 528 |
529 | ``` 530 | 531 | ### Google Apps Script side: `Code.gs` 532 | 533 | ```javascript 534 | function doPost(e) { 535 | const obj = { 536 | formData: e, 537 | spreadsheetId: "###spreadsheetId###", 538 | ignoreHeader: true, 539 | }; 540 | const res = HtmlFormApp.appendFormData(obj); 541 | return ContentService.createTextOutput( 542 | JSON.stringify({ message: "Done", row: res.range.getRow() }) 543 | ); 544 | } 545 | ``` 546 | 547 | 1. Please copy and paste this script to the script editor of Google Apps Script. Of course, you can use both the standalone type and the container-bound script type as the Google Apps Script project. And please set your Spreadsheet ID. 548 | 549 | 2. Please deploy Web Apps as `Execute as: Me` and `Who has access to the app: Anyone`. You can see how to deploy Web Apps at [here](https://developers.google.com/apps-script/guides/web). 550 | 551 | - When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this. 552 | - You can see the detail of this in the report of "[Redeploying Web Apps without Changing URL of Web Apps for new IDE](https://gist.github.com/tanaikech/ebf92d8f427d02d53989d6c3464a9c43)". 553 | 554 | 3. Please open your HTML file using your browser. When you input the values into the dialog and click the submit button, the values are put on the 1st sheet of the Spreadsheet. 555 | 556 | - If you want to put the values using the header row, please set the header row to the Spreadsheet and remove `ignoreHeader: true,`. 557 | 558 | ## IMPORTANT 559 | 560 | - **This library uses V8 runtime. So please enable V8 at the script editor.** 561 | 562 | - In the above method, when a file is uploaded, the maximum file size is 50 MB because of the current specification on the Google side. Please be careful about this. 563 | 564 | ## Limitations 565 | 566 | As a limitation, in the current stage, the maximum blob size for creating files using Google Apps Script is 50 MB. So when the large file is uploaded, the file cannot be used. Please be careful about this. 567 | 568 | --- 569 | 570 | 571 | 572 | # Licence 573 | 574 | [MIT](LICENCE) 575 | 576 | 577 | 578 | # Author 579 | 580 | [Tanaike](https://tanaikech.github.io/about/) 581 | 582 | 583 | 584 | # Update History 585 | 586 | - v1.0.0 (February 1, 2022) 587 | 588 | 1. Initial release. 589 | 590 | - v1.0.1 (May 29, 2023) 591 | 592 | 1. When multiple files are uploaded, each URL was set as the hyperlink. 593 | 594 | - v1.0.2 (October 17, 2023) 595 | 596 | 1. The 2nd argument `row` of `appendFormData(object, row)` was added. This is from [this suggestion](https://github.com/tanaikech/HtmlFormApp/issues/1). When `row` is used, the value is put into the specific row of the Spreadsheet. In this case, please set the value of `row` more than 1. 597 | 598 | - In this case, the submitted row can be forcefully put into the specific row of Google Spreadsheet. So, when you run `appendFormData(object, row)` by the constant value of `row`, the submitted row is put into the same row. Please be careful about this. 599 | 600 | [TOP](#top) 601 | -------------------------------------------------------------------------------- /appsscript.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeZone": "Asia/Tokyo", 3 | "dependencies": {}, 4 | "exceptionLogging": "STACKDRIVER", 5 | "runtimeVersion": "V8" 6 | } 7 | -------------------------------------------------------------------------------- /images/fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanaikech/HtmlFormApp/d216fe7229edb6b10cccc8d350bfc79c2929c9f9/images/fig1.png --------------------------------------------------------------------------------