如何将参数传递给html? [英] How to pass a parameter to html?

查看:205
本文介绍了如何将参数传递给html?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用文件选取器的脚本,但我需要传递一个名为userId的特定参数,并将其保存为调用脚本中的全局变量。由于调用是异步的,因此我无法访问这个参数。是否有从html文件访问参数或将此参数传递给html?



我可能混合模板html和非模板(即 https://developers.google.com/apps-script/guides/html/templates https://developers.google.com/apps-script/guides/html/)但我需要解决这个问题。

赞赏任何帮助。



以下是调用代码(通过电子表格中的菜单项启动):

 函数syncStudentsFile(userId,ss){
scriptUser_(userId ); // save userId
Logger.log('SRSConnect:syncStudentsFile:userId:'+ userId); // userId在这里是正确的
var ss = SpreadsheetApp.getActiveSpreadsheet();
var html = HtmlService.createHtmlOutputFromFile('PickerSync.html')
.setWidth(600).setHeight(425);
SpreadsheetApp.getUi()。showModalDialog(html,'选择一个文件');
}

函数scriptUser_(userId){
if(userId!== undefined)
sUserId = userId; //全局变量
try {return sUserId; } catch(e){return undefined; }
}

函数getOAuthToken(){//由Picker使用
DriveApp.getRootFolder();
return ScriptApp.getOAuthToken();
}

这里是html picker文件:

 < link rel =stylesheethref =https://ssl.gstatic.com/docs/script/css/add-ons.css> 

< script type =text / javascript>
var DEVELOPER_KEY ='..............';
var DIALOG_DIMENSIONS = {width:600,height:425};
var pickerApiLoaded = false;

/ **
*加载Google Picker API。
* /
gapi.load('picker',{'callback':function(){
pickerApiLoaded = true;
}});

/ **
*从服务器端脚本获取用户的访问令牌,以便
*可以传递给Picker。这种技术使Picker不需要
*显示自己的授权对话框,但只有在Picker需要的OAuth范围
*在Apps脚本中可用时才可能。否则,您的Picker代码
*将需要声明自己的OAuth范围。
* /
function getOAuthToken(){
google.script.run.withSuccessHandler(createPicker)
.withFailureHandler(showError).getOAuthToken();
}

/ **
*创建一个可以访问用户电子表格的选取器。此功能
*使用高级选项来隐藏Picker的左侧导航面板和
*默认标题栏。
*
* @param {string}令牌一个OAuth 2.0访问令牌,它允许Picker访问在addView调用中指定的
*文件类型。
* /
function createPicker(token){
if(pickerApiLoaded&& token){
var uploadView = new google.picker.DocsUploadView();
var picker = new google.picker.PickerBuilder()
//指示选取器仅显示云端硬盘中的电子表格。对于其他
//视图,请参阅https://developers.google.com/picker/docs/#otherviews
.addView(google.picker.ViewId.DOCS)
.addView(google ())
.addView )
//指示Picker填充对话框,减去边框的2个像素。
.setSize(DIALOG_DIMENSIONS.width - 2,
DIALOG_DIMENSIONS.height - 2)
.build();
picker.setVisible(true);
} else {
showError('无法加载文件选择器。');
}
}

/ **
*从
*响应对象中提取所选文档的元数据的回调函数。有关响应对象的详细信息,请参阅
* https://developers.google.com/picker/docs/result
*
* @param {object} data响应对象。
* /
函数pickerCallback(data){
var action = data [google.picker.Response.ACTION];
if(action == google.picker.Action.PICKED){
var doc = data [google.picker.Response.DOCUMENTS] [0];
var id = doc [google.picker.Document.ID];
google.script.host.close();
// -------------->用户全局参数sUserId设置较早
google.script.run.PickerSyncFile(sUserId,id);
} else if(action == google.picker.Action.CANCEL){
google.script.host.close();
}
}

/ **
*在#result元素中显示错误消息。
*
* @param {string}消息要显示的错误消息。
* /
function showError(message){
document.getElementById('result')。innerHTML ='Error:'+ message;
}
< / script>

< div>
< script> getOAuthToken()< / script>
< p id ='result'>< / p>
< input type =buttonvalue =Closeonclick =google.script.host.close()/>
< / div>

以下是选择器代码:

 函数pickerSyncFile(userId,id){
Logger.log('userId:'+ userId); // BUG:它是空的
Logger.log('id:'+ id); // id从picker中很好的返回

//其余的代码在这里,但userId是不正确的
}


解决方案

最安全的方法是直接将所需数据传递给HTML。如果您使用属性或缓存服务,它可能会在多个并发用户下变得复杂或失败。

有许多技术将初始对象从服务器(.gs)传递到客户端(.html)。



使用HtmlTemplate,您可以执行以下操作:

//.gs file

  function doGet(){
var htmlTemplate = HtmlService.createTemplateFromFile('template-client');
htmlTemplate.dataFromServerTemplate = {first:hello,last:world};
var htmlOutput = htmlTemplate.evaluate()。setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setTitle('sample');
返回htmlOutput;
}

以及您的template-client.html文件中:

 <!DOCTYPE html> 

< script>
var data =<?!= JSON.stringify(dataFromServerTemplate)?> //将数据直接存储在javascript代码中
//示例用法
函数initialize(){
document.getElementById(myTitle)。innerText = data.first + - + data.last;
//或使用jquery:$(#myTitle)。text(data.first + - + data.last);
}
//使用onload或在文档加载后使用jquery调用初始化
window.onload = initialize;
< / script>


< html>
< body>
< H2 id =myTitle>< / H2>
< / body>
< / html>

通过在HtmlOutput中添加一个隐藏的div,也可以不使用模板:



// .gs文件:

$ p $函数appendDataToHtmlOutput(data,htmlOutput ,idData){
if(!idData)
idData =mydata_htmlservice;

//数据在字符串化后被编码,以保证一个安全的字符串永远不会与html发生冲突。
//缺点:存储容量增加了大约30%。如果这是一个问题(当传递巨大的对象时),你可能会使用base94
//甚至是base128编码,但这需要更多的代码并可能有问题,请参阅http://stackoverflow.com/questions/6008047/why- dont-people-use-base128
var strAppend =< div id ='+ idData +'style ='display:none;'> + Utilities.base64Encode(JSON.stringify(data))+< / div>;
返回htmlOutput.append(strAppend);


$ b //示例用法:
函数doGet(){
var htmlOutput = HtmlService.createHtmlOutputFromFile('html-sample')
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setTitle('sample');

//数据可以是任何(可序列化的)javascript对象。
//如果你的数据是一个本地值(比如单个数字)传递一个像{num:myNumber}这样的对象
var data = {first:hello,last:world};
// appendDataToHtmlOutput修改html并返回相同的htmlOutput对象
返回appendDataToHtmlOutput(data,htmlOutput);
}

以及您的output-client.html中:

 <!DOCTYPE html> 
< script>
/ **
* getDataFromHtml
*
*输入
* idData:可选。数据元素的id。默认为mydata_htmlservice
*
*返回
*存储的数据对象
* /
函数getDataFromHtml(idData){
if(!idData)
idData =mydata_htmlservice;
var dataEncoded = document.getElementById(idData).innerHTML;
var data = JSON.parse(atob(dataEncoded));
返回数据;

// getDataFromHtml的示例用法
函数initialize(){
var data = getDataFromHtml();
document.getElementById(myTitle)。innerText = data.first + - + data.last;
//或使用jquery:$(#myTitle)。text(data.first + - + data.last);
}
//使用onload或在文档加载后使用jquery调用初始化
window.onload = initialize;
< / script>
< html>
< body>
< H2 id =myTitle>< / H2>
< / body>
< / html>



这两个方法在我做的这个小github中进行了比较和更好的解释:
https://github.com/zmandel/htmlService-get-set-data


I have a script that uses the file picker but I need to pass a specific parameter which is called userId and is kept as a global variable in the calling script. As the calls are asynchronous it seems I cannot access this parameter. Is there away to access the parameter from the html file or pass this parameter to the html?

I might be mixing templated html and non templated (i.e. https://developers.google.com/apps-script/guides/html/templates and https://developers.google.com/apps-script/guides/html/) but I need to solve this issue.

Appreciate any help.

Here is the calling code (initiated through a menu item in a spreadsheet):

function syncStudentsFile(userId, ss) {
  scriptUser_(userId);  // save userId
  Logger.log('SRSConnect : syncStudentsFile : userId:'+userId);  // userId is correct here
  var ss = SpreadsheetApp.getActiveSpreadsheet();  
  var html = HtmlService.createHtmlOutputFromFile('PickerSync.html')
    .setWidth(600).setHeight(425);
  SpreadsheetApp.getUi().showModalDialog(html, 'Select a file');
}

function scriptUser_(userId) {
  if (userId !== undefined)
    sUserId = userId; // Global variable
  try { return sUserId; } catch (e) { return undefined; }
}

function getOAuthToken() {  // used by Picker
  DriveApp.getRootFolder();
  return ScriptApp.getOAuthToken();
}

Here is the html picker file:

<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css">

<script type="text/javascript">
  var DEVELOPER_KEY = '..............';
  var DIALOG_DIMENSIONS = {width: 600, height: 425};
  var pickerApiLoaded = false;

  /**
   * Loads the Google Picker API.
   */
  gapi.load('picker', {'callback': function() {
    pickerApiLoaded = true;
  }});

  /**
   * Gets the user's access token from the server-side script so that
   * it can be passed to Picker. This technique keeps Picker from needing to
   * show its own authorization dialog, but is only possible if the OAuth scope
   * that Picker needs is available in Apps Script. Otherwise, your Picker code
   * will need to declare its own OAuth scopes.
   */
  function getOAuthToken() {
    google.script.run.withSuccessHandler(createPicker)
        .withFailureHandler(showError).getOAuthToken();
  }

  /**
   * Creates a Picker that can access the user's spreadsheets. This function
   * uses advanced options to hide the Picker's left navigation panel and
   * default title bar.
   *
   * @param {string} token An OAuth 2.0 access token that lets Picker access the
   *     file type specified in the addView call.
   */
  function createPicker(token) {
    if (pickerApiLoaded && token) {
      var uploadView = new google.picker.DocsUploadView();
      var picker = new google.picker.PickerBuilder()
          // Instruct Picker to display only spreadsheets in Drive. For other
          // views, see https://developers.google.com/picker/docs/#otherviews
          .addView(google.picker.ViewId.DOCS)
          .addView(google.picker.ViewId.RECENTLY_PICKED)
          .addView(uploadView)
          .hideTitleBar()
          .setOAuthToken(token)
          .setDeveloperKey(DEVELOPER_KEY)
          .setCallback(pickerCallback)
          // Instruct Picker to fill the dialog, minus 2 pixels for the border.
          .setSize(DIALOG_DIMENSIONS.width - 2,
              DIALOG_DIMENSIONS.height - 2)
          .build();
      picker.setVisible(true);
    } else {
      showError('Unable to load the file picker.');
    }
  }

  /**
   * A callback function that extracts the chosen document's metadata from the
   * response object. For details on the response object, see
   * https://developers.google.com/picker/docs/result
   *
   * @param {object} data The response object.
   */
  function pickerCallback(data) {
    var action = data[google.picker.Response.ACTION];
    if (action == google.picker.Action.PICKED) {
      var doc = data[google.picker.Response.DOCUMENTS][0];
      var id = doc[google.picker.Document.ID];
      google.script.host.close();
      // --------------> user global parameter sUserId set earlier
      google.script.run.PickerSyncFile(sUserId, id);
    } else if (action == google.picker.Action.CANCEL) {
      google.script.host.close();
    }
  }

  /**
   * Displays an error message within the #result element.
   *
   * @param {string} message The error message to display.
   */
  function showError(message) {
    document.getElementById('result').innerHTML = 'Error: ' + message;
  }
</script>

<div>
  <script>getOAuthToken()</script>
  <p id='result'></p>
  <input type="button" value="Close" onclick="google.script.host.close()" />
</div>

Here is the picker code:

function pickerSyncFile(userId, id) {
  Logger.log('userId:'+userId);  // BUG: it is null
  Logger.log('id:'+id);  // id returned well from picker

  // rest of code here but userId was is incorrect
}

解决方案

The safest way is to pass the needed data to the HTML directly. If you use properties or cache service it can get complex or fail under multiple simultaneous users.
There are many techniques to pass an initial object from the server (.gs) to the client (.html).

Using HtmlTemplate, you may do:
//.gs file

function doGet() {
    var htmlTemplate = HtmlService.createTemplateFromFile('template-client');
    htmlTemplate.dataFromServerTemplate = { first: "hello", last: "world" };
    var htmlOutput = htmlTemplate.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME)
        .setTitle('sample');
    return htmlOutput;
}

and in your template-client.html file:

<!DOCTYPE html>

<script>
    var data = <?!= JSON.stringify(dataFromServerTemplate) ?>; //Stores the data directly in the javascript code
    // sample usage
    function initialize() {
        document.getElementById("myTitle").innerText = data.first + " - " + data.last;
        //or use jquery:  $("#myTitle").text(data.first + " - " + data.last);
    }
    // use onload or use jquery to call your initialization after the document loads
    window.onload = initialize;
</script>


<html>
<body>
    <H2 id="myTitle"></H2>
</body>
</html>

It is also possible to do it without using templating, by appending a hidden div to an HtmlOutput:

//.gs file:

function appendDataToHtmlOutput(data, htmlOutput, idData) {
    if (!idData)
        idData = "mydata_htmlservice";

    // data is encoded after stringifying to guarantee a safe string that will never conflict with the html.
    // downside: increases the storage size by about 30%. If that is a concern (when passing huge objects) you may use base94
    // or even base128 encoding but that requires more code and can have issues, see http://stackoverflow.com/questions/6008047/why-dont-people-use-base128
    var strAppend = "<div id='" + idData + "' style='display:none;'>" + Utilities.base64Encode(JSON.stringify(data)) + "</div>";
    return htmlOutput.append(strAppend);
}


// sample usage:
function doGet() {
    var htmlOutput = HtmlService.createHtmlOutputFromFile('html-sample')
        .setSandboxMode(HtmlService.SandboxMode.IFRAME)
        .setTitle('sample');

    // data can be any (serializable) javascript object.
    // if your data is a native value (like a single number) pass an object like {num:myNumber}
    var data = { first: "hello", last: "world" };
    // appendDataToHtmlOutput modifies the html and returns the same htmlOutput object
    return appendDataToHtmlOutput(data, htmlOutput);
}

and in your output-client.html:

<!DOCTYPE html>
<script>
    /**
    * getDataFromHtml
    *
    * Inputs
    * idData: optional. id for the data element. defaults to "mydata_htmlservice"
    *
    * Returns
    * The stored data object
    */
    function getDataFromHtml(idData) {
        if (!idData)
            idData = "mydata_htmlservice";
        var dataEncoded = document.getElementById(idData).innerHTML;
        var data = JSON.parse(atob(dataEncoded));
        return data;
    }
    // sample usage of getDataFromHtml
    function initialize() {
        var data = getDataFromHtml();
        document.getElementById("myTitle").innerText = data.first + " - " + data.last;
        //or use jquery:  $("#myTitle").text(data.first + " - " + data.last);
    }
    // use onload or use jquery to call your initialization after the document loads
    window.onload = initialize;
</script>
<html>
<body>
    <H2 id="myTitle"></H2>
</body>
</html>


Both methods are compared and better explained in this little github I made: https://github.com/zmandel/htmlService-get-set-data

这篇关于如何将参数传递给html?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆