Passing information to server-side function in a G

2019-07-11 19:28发布

I'm working on a Google Docs Add-On based on Google's Quickstart tutorial. I'm trying to change the workflow of the Add On in the tutorial to append a new page and then insert a translation on that new page rather than the line-by-line workflow.

I have a script working in single documents but I'm having a hard time moving it to the Add On architecture. I think it's something to do with passing selections from client-side JS to the server-side script doing the translation.

Here's the translate script

function translate(origin, dest, savePrefs) {
  Logger.log('Starting the script');
  if (savePrefs == true) {
    var userProperties = PropertiesService.getUserProperties();
    userProperties.setProperty('originLang', origin);
    userProperties.setProperty('destLang', dest);
    Logger.log(origin,dest);
  }

  var doc = DocumentApp.getActiveDocument();
  var body = doc.getBody();

  // Add a page break for the translated material.
  body.appendPageBreak();

  // Get the number of elements in the document
  var elements = body.getNumChildren();
  Logger.log('Got the page elements');

  // Use the number to loop through each element in the document.
  for( var i=0;i<elements;i++) {
   var element = body.getChild(i).copy();
    var type = element.getType();
    Logger.log('Element Types were successful. Starting tests.');    

    // Test each type for a child element and run script based on the result
    // Images are nested in a paragraph as a child, so the second `if` makes
    // sure there is no image present before moving to the next paragraph.
    if( type == DocumentApp.ElementType.PARAGRAPH ){
      if(element.asParagraph().getNumChildren() != 0 && element.asParagraph().getChild(0).getType() == DocumentApp.ElementType.INLINE_IMAGE) {
        var img = element.asParagraph().getChild(0).asInlineImage().getBlob();
        body.appendImage(img);
      } else if(element.asParagraph().getNumChildren() !=0 && element.asParagraph().getChild(0).getType() == DocumentApp.ElementType.INLINE_DRAWING) {
        var drawing = element.asParagraph().copy();
        body.appendParagraph(drawing);
      } else {
        var text = element.asParagraph().getText();
        Logger.log(text);
        var spn = LanguageApp.translate(text, origin, dest);
        body.appendParagraph(spn);
      }
    } else if(type == DocumentApp.ElementType.TABLE) {
      element.asTable().copy();
      body.appendTable(element);
    } else if(type == DocumentApp.ElementType.LIST_ITEM) {
      var list = element.asListItem().getText();
      body.appendListItem(LanguageApp.translate(list, origin, dest));
    }
  }

The client-side JS is:

$(function() {
    $('#run-translation').click(loadPreferences);
    google.script.run(runTranslation)
  });

function runTranslation() {
    this.disabled = true;
    var origin = $('input[name=origin]:checked').val();
    var dest = $('input[name=dest]:checked').val();
    var savePrefs = $('#save-prefs').is(':checked');
    google.script.run
      .runTranslation(origin, dest, savePrefs);
  }

If I hard-code the languages to use in translation into the server-side script, it works. But as soon as I try to use variables from the radio buttons, it doesn't run. I don't see any errors in the console and I can't run scripts from the editor to check the logs. How can I debug this code?

1条回答
时光不老,我们不散
2楼-- · 2019-07-11 20:04

Calling server-side functions from client Javascript

You've got a minor syntax error with google.run:

google.script.run(runTranslation)

It should be:

google.script.run
             .withFailureHander(failFunc) // Optional
             .withSuccessHander(succFunc) // Optional
             .serverFunction(optionalParams...);

The two Handler methods assign callback functions from your client-side JavaScript to be invoked in the case of success or failure of the server-side function. In both cases, the client-side function is provided as the only parameter.

The server-side function you want to communicate with is presented as if it is a method itself, with optional parameters to be passed across the divide.

The simplest case for you is:

google.script.run
             .translate(origin, dest, savePrefs);

(You had runTranslation in your posted code, but the server-side function is named translate()... I assume that's the right one.)

Now, this might will not take care of all your problems, so you wisely asked about debugging...

Debugging asynchronous client / server code in Google Apps Script

The provided debug environment isn't enough for debugging this sort of client / server exchange. There are also weaknesses in the Debugger for use with asynchronous execution - you can read more about that in Not seeing logs from onEdit trigger.

The simplest tool to get you debugging in this case would be to write logs to a spreadsheet

/**
 * Write message and timestamp to log spreadsheet.
 * From: https://stackoverflow.com/a/32212124/1677912
 */
function myLog( message ) {
  var ss = SpreadsheetApp.openById( logSpreadsheetId );
  var logSheet = ss.getSheetByName("Log") || ss.insertSheet("Log");
  logSheet.appendRow([ new Date(), message );
}

You can call this from your client-side Javascript thusly:

google.script.run.myLog( "CLIENT: " + message );

That's a basic approach, but you can extend it more through use of utility functions and the BetterLog library. See more about that in my blog entry Did you know? (You can log to a spreadsheet from client JavaScript!)

查看更多
登录 后发表回答