Google Apps Script: passing error into templated H

2019-08-11 01:30发布

问题:

I have the following code:

code.gs:

function onOpen() {
    var ui = SpreadsheetApp.getUi();
    ui.createMenu('My Menu')
      .addItem('Test', 'showTestForm')
      .addToUi();
}

function showTestForm() {
    var html = HtmlService.createHtmlOutputFromFile('TestForm');
    SpreadsheetApp.getUi().showModalDialog(html, 'TEST');
}

function Test(formObject){
  Logger.log("TEST")
  var a = new Error( "Allready present "+ formObject);
  a.error_code = 99;
  Logger.log(JSON.stringify(a));
  throw a;
}

TestForm.html

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8" />
    <base target="_top">
    <script>
        function onFailure(error) {   
            var keys = Object.keys(error);
            alert(JSON.stringify(keys));
            alert(JSON.stringify(error.message));
            alert(JSON.stringify(error.error_code));
        }

        function onSuccess() {
            alert("Success");
        }
    </script>
</head>
<body>
    <input type="submit" value="Save" onclick="google.script.run.withFailureHandler(onFailure).withSuccessHandler(onSuccess).Test('1')" />
    <input type="button" value="Close" onclick="google.script.host.close()" />
</body>
</html>

When I open TestForm from menu and press "Save" I've got following log from Logger:

[18-12-24 23:08:24:765 PST] TEST
[18-12-24 23:08:24:766 PST] {"message":"Allready present 1","error_code":99}

So I see, that error object have properties 'message' and 'error_code'. But in browser I've got following alerts:

["name"]
"Error: Allready present 1"
undefined

So I see, that recived error object has only one empty (i've checked) property "name". But if I but refer to the property "message, I've got string like in original object (but not the same). And it looks like that object haven't poperty "error_code". What's the matter?

回答1:

I thought you might like a complete working example as I know this stuff can be quite frustrating.

This a simple example templated HTML file that can be used as a dialog or a webapp. All it does is create a Google Doc file with todays date in the header and footer of each page and it puts the file into the same directory as the spreadsheet which contains the script. That's it. I use the Chrome Browser. I don't even care if my scripts won't run on another browser.

Here's the HTML: (FileName:'docwithdate.html')

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <?!= include('resources') ?>
    <?!= include('css') ?>
  </head>
  <body>
    <?!= include('form') ?>
    <?!= include('script') ?>
  </body>
</html>

The Resources: (FileName: 'resources.html')

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

The CSS: (FileName: 'css.html')

<style>
body {background-color:#ffffff;}
input[type="button"]{padding:0 0 2px 0;}
</style>

The Form: (FileName: form.html) This is probably push the templating idea a little far.

<form id="myForm" onsubmit="event.preventDefault();processForm(this);" >
  <input type="text" id="txt1" name="filename" />
  <input id="btn" type="submit" value="Submit" />
</form>

The Javascript: [FileName: 'script.html')

<script>
    function createFile(){
      var name=document.getElementById('filename').value;
      google.script.run
      .withSuccessHandler(function(rObj){
        var html='<br /><a href="'+ rObj.url +'" onClick="google.script.host.close();">Go To File:' + rObj.filename + '</a>';
        $(html).appendTo("body");
      })
      .createTemplatedGoogleDoc(name);
    }

    function getInputObject(obj) {//I'm probably doing something wrong here. But this is what I had to do to get the object with the properties that I desired.  So if you have another way.  Go for it.
      var rObj={};
      for(var i=0;i<Object.keys(obj).length;i++){
        if(obj[i].type=="text"){
          rObj[obj[i].name]=obj[i].value;
        }
        console.log('Object.keys(rObj): %s',Object.keys(rObj).join(', '));
      }
      return rObj;
    }

    function processForm(obj){
      var fObj=getInputObject(obj);
      var name=fObj.filename;
      google.script.run
      .withSuccessHandler(function(rObj){
        document.getElementById("btn").disabled=true;
        var html='<br /><a href="'+ rObj.url +'" onClick="google.script.host.close();">Go To File:' + rObj.filename + '</a>';
        $(html).appendTo("body");
      })
      .createTemplatedGoogleDoc(name);
    }
    console.log('My Code');
</script>

The Google Script: (FileName: Code.gs)

function onOpen(){
  SpreadsheetApp.getUi().createMenu('My Menu')
  .addItem("Open Templated Google Doc", 'showMyDialog')
  .addToUi()
}

function createTemplatedGoogleDoc(name){
  Logger.log(name);
  var doc=DocumentApp.create(name);//Creates a google doc
  var fldrs=DriveApp.getFileById(SpreadsheetApp.getActive().getId()).getParents();
  while(fldrs.hasNext()){
    var fldr=fldrs.next();
    if(fldr.getName()=="Create Templated Google Doc App"){
      var folder=fldr;
    }
  }
  Drive.Files.update({"parents": [{"id": folder.getId()}]}, doc.getId());//puts doc file into same directory as the spreadsheet that contains the script
  doc.addHeader().appendParagraph(Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "E MMM dd, yyyy"));
  doc.addFooter().appendParagraph(Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "E MMM dd, yyyy"));
  //doc.getBody().getChild(0).removeFromParent();
  doc.saveAndClose()
  var rObj={url:doc.getUrl(),filename:doc.getName()}
  return rObj;
}

function showMyDialog(){
  var ui=HtmlService.createTemplateFromFile('docwithdate').evaluate();
  SpreadsheetApp.getUi().showModelessDialog(ui, 'My Doc with Date');
}

function doGet(){//if you want a web app this is helpful
  return HtmlService.createTemplateFromFile('docwithdate').evaluate();
}

function include(filename){//this is the include that the template uses
  return HtmlService.createHtmlOutputFromFile(filename).getContent();
}

It's a pretty simple script. I hope it helps you get a start.



回答2:

In accordance with the proposal of @TheMaster it is necessary to do this:

code.gs

function Test(formObject){
    var a = new Error( JSON.stringify({msg:"Allready present "+ formObject,code:99}));
    throw a;
}

TestForm.html

// removing "Error: " from message string to get our json back
var json = error.message.replace("Error: ",'')
var msg = JSON.parse(json).msg;
var code = JSON.parse(json).code;

That is, we put json into the attribute message of the Error object, and then, by cutting our json, we parse it and get the necessary values.

This is not exactly the answer to the question, but a good way to solve the problem.