Report: Values to transfer between Javascript and Google Apps Script with google.script.run

Gists

At the Google Apps Script project, the values can be transferred from HTML to Google Apps Script using google.script.run with Javascript. In this case, unfortunately, the values of all types cannot be transferred. In the official document, it says as follows. Ref

Most types are legal, but not Date, Function, or DOM element besides form; see description

Legal parameters are JavaScript primitives like a Number, Boolean, String, or null, as well as JavaScript objects and arrays that are composed of primitives, objects, and arrays. A form element within the page is also legal as a parameter, but it must be the function’s only parameter. Requests fail if you attempt to pass a Date, Function, DOM element besides a form, or other prohibited type, including prohibited types inside objects or arrays. Objects that create circular references will also fail, and undefined fields within arrays become null. Note that an object passed to the server becomes a copy of the original. If a server function receives an object and changes its properties, the properties on the client are not affected.

From this document, it can know about the values (number, boolean, string, array, and object including them) for often can be used. But, there are the values of more types in Javascript. It is considered that when the values that can be used for this situation are clearly known, it will be useful for a lot of developers. This report introduces the values that can be used for transferring between HTML and Google Apps Script with google.script.run.

1. From HTML to Google Apps Script

This section introduces the values that can be transferred from HTML to Google Apps Script with google.script.run. The sample script can be seen in the “Appendix” section. As a result, the following table could be obtained.

Passed values

Input value Input type Output value Output type Result (Pass or Fail)
123 Number 123 Number Pass
"sample" String "sample" String Pass
true Boolean true Boolean Pass
new Array(1).fill("sample") Array ["sample"] Array Pass
new Array(1).fill(new Array(1).fill("sample")) Array [["sample"]] Array Pass
{ key: "value" } Object { "key": "value" } Object Pass
document.getElementById("form") Form object including input tag { "sample text": "sample value" } Object Pass
null Null null Null Pass

Failed values

Input value Input type Output value Output type Result (Pass or Fail)
new Number(123) Number Fail
new String("sample") String Fail
new RegExp("^.*$") RegExp Fail
function () { return "ok" } Function Fail
document.getElementById("text") Input tag object Fail
new Date() Date Fail
[0, 1, 2, new Date(), 4, 5] Array including date Fail
new Map([["a", "b"]]) Map Fail
new Set(["a", "b"]) Set Fail
new Promise(res => res("ok")) Promise Fail
new Blob(["sample text"], { type: 'text/plain' }) Blob Fail
new Blob(["sample text"], { type: 'text/plain' }).arrayBuffer() Array buffer Fail
new Int8Array(buffer) Int8Array Fail
new Uint8Array(buffer) Uint8Array Fail
new Blob(["sample text"], { type: 'text/plain' }).stream() Stream object Fail
undefined undefined null Null Fail
  • In the case of [0, 1, 2, new Date(), 4, 5], an error like Failed due to illegal value in property: 3 occurred. By this, it is found that an error occurs at the index of 3.

2. From Google Apps Script to HTML

This section introduces the values that can be transferred from Google Apps Script to HTML with google.script.run. The sample script can be seen in the “Appendix” section. As a result, the following table could be obtained.

In this section, the input value and output value are the opposition from the above section. Please be careful about this.

Passed values

Input value Input type Output value Output type Result (Pass or Fail)
123 Number 123 Number Pass
"sample" String "sample" String Pass
true Boolean true Boolean Pass
new Array(1).fill("sample") Array ["sample"] Array Pass
new Array(1).fill(new Array(1).fill("sample")) Array [["sample"]] Array Pass
{ key: "value" } Object {"key":"value"} Object Pass
Utilities.newBlob("sample text", 'text/plain').getBytes() Bytes [115,97,109,112,108,101,32,116,101,120,116] Array Pass
new Int8Array(bytes) Int8Array {"0":115,"1":97,"2":109,"3":112,"4":108,"5":101,"6":32,"7":116,"8":101,"9":120,"10":116} Object Pass
null Null null Null Pass

Failed values

Input value Input type Output value Output type Result (Pass or Fail)
new Number(123) Number {} Object Fail
new String("sample") String {"0":"s","1":"a","2":"m","3":"p","4":"l","5":"e"} Object Fail
new RegExp("^.*$") RegExp {} Object Fail
function () { return "ok" } Function "function () { return \"ok\" }" String Fail
new Date() Date No value Fail
[0, 1, 2, new Date(), 4, 5] Array including date No value Fail
new Map([["a", "b"]]) Map {} Object Fail
new Set(["a", "b"]) Set {} Object Fail
new Promise(res => res("ok")) Promise "ok" String Fail
Utilities.newBlob("sample text", 'text/plain') Blob {"setName":"function () { [native code] }","setBytes":"function () { [native code] }",,,} Object Fail
new Uint8Array(bytes).buffer Array buffer {} Object Fail
new Uint8Array(bytes) Uint8Array "サーバー エラーが発生しました。しばらくしてからもう一度試してください。" Fail
undefined undefined null Null Fail
SpreadsheetApp.getActiveSpreadsheet() Class Spreadsheet object No value Fail

Summary

This report investigated the values to transfer between Javascript and Google Apps Script with google.script.run. From this report, the following result was obtained.

  • In the case that the values are transferred from HTML to Google Apps Script, the following values can be transferred.

    • Number
    • String
    • Boolean
    • Array
    • JSON object constructed with numbers, values, and boolean
    • Form object
    • Null
  • In the case that the values are transferred from Google Apps Script to HTML, the following values can be transferred.

    • Number
    • String
    • Boolean
    • Array
    • JSON object constructed with numbers, values, and boolean
    • Byte array
    • Int8Array
    • Null
  • As an important point, when the failed value is included in an array and JSON object, an error occurs. Please be careful about this.

  • When you want to transfer the failed values, it is required to convert them to the passed values like string and byte array. By this, you can transfer between HTML and Google Apps Script with google.script.run.

When you develop a script for transferring values between HTML and Google Apps Script with google.script.run, when the values cannot be correctly transferred, if this report is useful, I’m glad.

Appendix

Script of “From HTML to Google Apps Script”

Google Apps Script

const doGet = (_) => HtmlService.createHtmlOutputFromFile("index");

const sample1 = (e) => {
  const obj = ["null", "undefined", "", "0"];
  if (e) {
    const type = typeof e;
    return { response: e, type };
  }
  return { response: obj[[null, undefined, "", 0].findIndex((f) => f === e)] };
};

HTML & Javascript

<script src="https://cdn.jsdelivr.net/gh/tanaikech/syncGoogleScriptRun@master/syncGoogleScriptRun.min.js"></script>
<form id="form">
  <input type="text" value="sample value" name="sample text" id="text" />
</form>
<input type="button" value="run" onclick="run()" />

<script>
  async function run() {
    const blob = new Blob(["sample text"], { type: "text/plain" });
    const buffer = await blob.arrayBuffer();
    const obj = {
      number1: 123,
      number2: new Number(123),
      string1: "sample",
      string2: new String("sample"),
      bool: true,
      regex: new RegExp("^.*$"),
      array1dObject: new Array(1).fill("sample"),
      array2dObject: new Array(1).fill(new Array(1).fill("sample")),
      jsonObject: { key: "value" },
      functionObject: function () {
        return "ok";
      },
      formObject: document.getElementById("form"),
      inputTagObject: document.getElementById("text"),
      date1Object: new Date(),
      date2Object: [0, 1, 2, new Date(), 4, 5],
      mapObject: new Map([["a", "b"]]),
      setObject: new Set(["a", "b"]),
      promiseObject: new Promise((res) => res("ok")),
      blobObject: blob,
      bufferObject: buffer,
      int8ArrayObject: new Int8Array(buffer.buffer),
      uint8ArrayObject: new Uint8Array(buffer.buffer),
      streamObject: await blob.stream(),
      nullValue: null,
      undefinedValue: undefined,
    };
    const ret = {};
    const err = [];
    const noResponse = [];
    for (let i in obj) {
      const r = await syncGoogleScriptRun({
        gasFunction: "sample1",
        arguments: obj[i],
      }).catch((e) => (err[i] = { error: e.message }));
      if (r) {
        ret[i] = r;
      } else {
        noResponse.push(i);
      }
    }
    const keys = Object.keys(obj);
    const res = keys.reduce((o, k) => ((o[k] = err[k] || ret[k]), o), {});

    console.log(res);
    console.log(err);
    console.log(noResponse);
  }
</script>

Result

console.log(res)

{
  "number1": { "type": "number", "response": 123 },
  "number2": { "error": "Failed due to illegal value in property: 0" },
  "string1": { "type": "string", "response": "sample" },
  "string2": { "error": "Failed due to illegal value in property: 0" },
  "bool": { "type": "boolean", "response": true },
  "regex": { "error": "Failed due to illegal value in property: 0" },
  "array1dObject": { "type": "object", "response": ["sample"] },
  "array2dObject": { "type": "object", "response": [["sample"]] },
  "jsonObject": { "type": "object", "response": { "key": "value" } },
  "functionObject": { "error": "Failed due to illegal value in property: 0" },
  "formObject": {
    "type": "object",
    "response": { "sample text": "sample value" }
  },
  "inputTagObject": { "error": "Failed due to illegal value in property: 0" },
  "date1Object": { "error": "Failed due to illegal value in property: 0" },
  "date2Object": { "error": "Failed due to illegal value in property: 3" },
  "mapObject": { "error": "Failed due to illegal value in property: 0" },
  "setObject": { "error": "Failed due to illegal value in property: 0" },
  "promiseObject": { "error": "Failed due to illegal value in property: 0" },
  "blobObject": { "error": "Failed due to illegal value in property: 0" },
  "bufferObject": { "error": "Failed due to illegal value in property: 0" },
  "int8ArrayObject": { "error": "Failed due to illegal value in property: 0" },
  "uint8ArrayObject": { "error": "Failed due to illegal value in property: 0" },
  "streamObject": { "error": "Failed due to illegal value in property: 0" },
  "nullValue": { "response": "null" },
  "undefinedValue": { "response": "null" }
}

console.log(err)

[]

console.log(noResponse)

[]

Script of “From Google Apps Script to HTML”

Google Apps Script

const doGet = (_) => HtmlService.createHtmlOutputFromFile("index");

function sample2(e) {
  const blob = Utilities.newBlob("sample text", "text/plain");
  const bytes = blob.getBytes();
  const buffer = new Uint8Array(bytes).buffer;
  const obj = {
    number1: 123,
    number2: new Number(123),
    string1: "sample",
    string2: new String("sample"),
    bool: true,
    regex: new RegExp("^.*$"),
    array1dObject: new Array(1).fill("sample"),
    array2dObject: new Array(1).fill(new Array(1).fill("sample")),
    jsonObject: { key: "value" },
    functionObject: function () {
      return "ok";
    },
    // formObject: document.getElementById("form"),
    // inputTagObject: document.getElementById("text"),
    date1Object: new Date(),
    date2Object: [0, 1, 2, new Date(), 4, 5],
    mapObject: new Map([["a", "b"]]),
    setObject: new Set(["a", "b"]),
    promiseObject: new Promise((res) => res("ok")),
    blobObject: blob,
    bytesObject: bytes,
    bufferObject: buffer,
    int8ArrayObject: new Int8Array(bytes),
    uint8ArrayObject: new Uint8Array(bytes),
    // streamObject: await blob.stream(),
    nullValue: null,
    undefinedValue: undefined,
    googleAppsScriptClassObject: SpreadsheetApp.getActiveSpreadsheet(),
  };
  return obj[e];
}

HTML & Javascript

<script src="https://cdn.jsdelivr.net/gh/tanaikech/syncGoogleScriptRun@master/syncGoogleScriptRun.min.js"></script>
<input type="button" value="run" onclick="run()" />

<script>
  async function run() {
    const keys = [
      "number1",
      "number2",
      "string1",
      "string2",
      "bool",
      "regex",
      "array1dObject",
      "array2dObject",
      "jsonObject",
      "functionObject",
      "date1Object",
      "date2Object",
      "mapObject",
      "setObject",
      "promiseObject",
      "blobObject",
      "bytesObject",
      "bufferObject",
      "int8ArrayObject",
      "uint8ArrayObject",
      "nullValue",
      "undefinedValue",
      "googleAppsScriptClassObject",
    ];
    const ret = {};
    const err = [];
    const noResponse = [];
    for (let i of keys) {
      const r = await syncGoogleScriptRun({
        gasFunction: "sample2",
        arguments: i,
      }).catch((e) => (err[i] = { error: e.message }));
      if (r) {
        const type = typeof r;
        ret[i] = { response: r, type };
      } else {
        noResponse.push(i);
      }
    }
    const res = keys.reduce((o, k) => ((o[k] = err[k] || ret[k]), o), {});

    console.log(res);
    console.log(err);
    console.log(noResponse);
  }
</script>

Result

console.log(res)

{
  "number1": { "response": 123, "type": "number" },
  "number2": { "response": {}, "type": "object" },
  "string1": { "response": "sample", "type": "string" },
  "string2": {
    "response": { "0": "s", "1": "a", "2": "m", "3": "p", "4": "l", "5": "e" },
    "type": "object"
  },
  "bool": { "response": true, "type": "boolean" },
  "regex": { "response": {}, "type": "object" },
  "array1dObject": { "response": ["sample"], "type": "object" },
  "array2dObject": { "response": [["sample"]], "type": "object" },
  "jsonObject": { "response": { "key": "value" }, "type": "object" },
  "functionObject": {
    "response": "function () { return \"ok\" }",
    "type": "string"
  },
  "mapObject": { "response": {}, "type": "object" },
  "setObject": { "response": {}, "type": "object" },
  "promiseObject": { "response": "ok", "type": "string" },
  "blobObject": {
    "response": {
      "setName": "function () { [native code] }",
      "setBytes": "function () { [native code] }",
      "getName": "function () { [native code] }",
      "getDataAsString": "function () { [native code] }",
      "getAllBlobs": "function () { [native code] }",
      "setDataFromString": "function () { [native code] }",
      "getBytes": "function () { [native code] }",
      "getAs": "function () { [native code] }",
      "isGoogleType": "function () { [native code] }",
      "getContentType": "function () { [native code] }",
      "toString": "function () { [native code] }",
      "setContentTypeFromExtension": "function () { [native code] }",
      "setContentType": "function () { [native code] }",
      "copyBlob": "function () { [native code] }"
    },
    "type": "object"
  },
  "bytesObject": {
    "response": [115, 97, 109, 112, 108, 101, 32, 116, 101, 120, 116],
    "type": "object"
  },
  "bufferObject": { "response": {}, "type": "object" },
  "int8ArrayObject": {
    "response": {
      "0": 115,
      "1": 97,
      "2": 109,
      "3": 112,
      "4": 108,
      "5": 101,
      "6": 32,
      "7": 116,
      "8": 101,
      "9": 120,
      "10": 116
    },
    "type": "object"
  },
  "uint8ArrayObject": {
    "error": "サーバー エラーが発生しました。しばらくしてからもう一度試してください。"
  }
}

console.log(err)

[]

console.log(noResponse)

[
  "date1Object",
  "date2Object",
  "nullValue",
  "undefinedValue",
  "googleAppsScriptClassObject"
]

Reference

 Share!