简体   繁体   English

从附加组件边栏中打开 Google 文件选择器

[英]Open Google File Picker from Add-on Sidebar

I am looking to load a Google Drive file picker by clicking a button within a Google Form add-on sidebar.我希望通过单击 Google 表单插件侧边栏中的按钮来加载 Google 云端硬盘文件选择器。

Problem问题

I have not been able to work out how to load the picker (in a modal dialog) directly from the sidebar, then callback the document id to the sidebar.我一直无法弄清楚如何直接从侧边栏加载选择器(在模式对话框中),然后将文档 ID 回调到侧边栏。

I have been able to successfully load a modal dialog and then load the picker from the modal dialog, however am struggling to understand how to load the picker directly from the sidebar.我已经能够成功加载模式对话框,然后从模式对话框加载选择器,但是我很难理解如何直接从侧边栏加载选择器。

Any guidance is appreciated.任何指导表示赞赏。 My current code is shown below.我当前的代码如下所示。

.gs file: .gs 文件:

function onOpen(e) {
  FormApp.getUi()
  .createAddonMenu()
  .addItem('Picker', 'showSidebar')
  .addToUi();
}

function showSidebar(){

    var ui = HtmlService.createHtmlOutputFromFile('Sidebar')
  .setSandboxMode(HtmlService.SandboxMode.IFRAME)
  .setTitle('Title');
  FormApp.getUi().showSidebar(ui);

}

function openPicker(){

  var html = HtmlService.createHtmlOutputFromFile('Picker')
  .setWidth(600)
  .setHeight(425)
  .setSandboxMode(HtmlService.SandboxMode.IFRAME);
  FormApp.getUi().showModalDialog(html, 'Select a file');

}


function getOAuthToken() {
  DriveApp.getRootFolder();
  return ScriptApp.getOAuthToken();
}

sidebar html侧边栏 html

<!DOCTYPE html>
<html>
  <head>
  <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css">
    <base target="_top">
  </head>
  <body>
    <button onclick='openPicker()'>Select a file</button>
  </body>
</html>

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<script>


function openPicker() {
google.script.run
.withSuccessHandler(success)
.withFailureHandler(failure)
.openPicker();
}

function success() {
console.log('success');
}

function failure() {
console.log('failure');
}

</script>

picker html选择器 html

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

    var DEVELOPER_KEY = '';
    var DIALOG_DIMENSIONS = {width: 600, height: 425};
    var pickerApiLoaded = false;


    function onApiLoad() {
      gapi.load('picker', {'callback': function() {
        pickerApiLoaded = true;
      }});
     }


    function getOAuthToken() {
      google.script.run
      .withSuccessHandler(createPicker)
      .withFailureHandler(showError)
      .getOAuthToken();
    }


    function createPicker(token) {
    console.log("here");
      if (pickerApiLoaded && token) {
        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.DOCUMENTS)
            // Hide the navigation panel so that Picker fills more of the dialog.
            .enableFeature(google.picker.Feature.NAV_HIDDEN)
            // Hide the title bar since an Apps Script dialog already has a title.
            .hideTitleBar()
            .setOAuthToken(token)
            .setDeveloperKey(DEVELOPER_KEY)
            .setCallback(pickerCallback)
            .setOrigin(google.script.host.origin)
            // 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.');
      }
    }


    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];
        var url = doc[google.picker.Document.URL];
        var title = doc[google.picker.Document.NAME];
        document.getElementById('result').innerHTML =
            '<b>You chose:</b><br>Name: <a href="' + url + '">' + title +
            '</a><br>ID: ' + id;
      } else if (action == google.picker.Action.CANCEL) {
        document.getElementById('result').innerHTML = 'Picker canceled.';
      }
    }


    function showError(message) {
      document.getElementById('result').innerHTML = 'Error: ' + message;
    }
  </script>
</head>
<body>
  <div>
    <button onclick='getOAuthToken()'>Select a file</button>
    <p id='result'></p>
  </div>
  <script src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
</body>
</html>

Here's the quick and dirty solution I came up with.这是我想出的快速而肮脏的解决方案。 My '.gs' file contains functions for displaying UI containers served via HtmlService and obtaining the OAuth token.我的“.gs”文件包含用于显示通过HtmlService 提供的UI 容器和获取 OAuth 令牌的函数。 Note that you can pass data models to HtmlTemplate objects before they get converted to raw HTML and passed to the client.请注意,您可以将数据模型传递给HtmlTemplate对象,然后再将它们转换为原始 HTML 并传递给客户端。 When we call the 'showSidebar' function from the custom menu, no arguments are passed so it's the first call.当我们从自定义菜单调用“showSidebar”函数时,没有传递任何参数,因此它是第一次调用。 If 'params' is not 'undefined', we know the call comes from the modal dialog.如果 'params' 不是 'undefined',我们就知道调用来自模态对话框。

Code.gs代码.gs

var ui = SpreadsheetApp.getUi()


//add custom menu to the spreadsheet;
function onOpen() {

ui.createMenu('Sidebar')
.addItem('Show sidebar', 'showSidebar')
.addToUi();

}

function showSidebar(params){

var sidebar = HtmlService.createTemplateFromFile('sidebar');
var model = "your selection"; //default message to be displayed

  if (params) { //if arguments are passed, it's a callback from modal dialog
    model = "You selected document with id: " +     
                  params.id + ", title: " + params.title + 
                  " url: " + params.url; 
  }  

sidebar.model = model; // pass model to the template
var htmlOutput = sidebar.evaluate(); //execute inline scriplets from the template to complete DOM construction. 
ui.showSidebar(htmlOutput); //pass resulting UI object to sidebar container

}

function showModalDialog() {

// produce HtmlOutput for modal dialog
var modalDialog = HtmlService.createTemplateFromFile('modal_dialog')
                             .evaluate()
                             .setWidth(600)
                             .setHeight(425)
                             .setSandboxMode(HtmlService.SandboxMode.IFRAME); 

ui.showModalDialog(modalDialog, 'Picker');


}

function getOAuthToken() {
  DriveApp.getRootFolder();
  return ScriptApp.getOAuthToken();
}

sidebar.html:侧边栏.html:

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
  <input type="button" id="button" value="Open picker">
  <div>  Selection:  <?!=  model ?> </div>  

  <script>

  window.onload = (function(){

  var button = document.getElementById('button');
  button.addEventListener('click', function(event){

  google.script.run
               .withSuccessHandler(function(){             

                 google.script.host.close();             

               })
               .showModalDialog(); 

  });


  })();  
  </script>
  </body>
</html>

The scriptlets between <? <?之间的小脚本?> will fire when 'evaluate()' is called on the object belonging to the HtmlTemplate class. ?>将在对属于HtmlTemplate类的对象调用'evaluate()' 时触发 There are several types of scriptlets - more on scriptlets here:有几种类型的脚本 - 更多关于这里的脚本:

https://developers.google.com/apps-script/guides/html/templates#scriptlets https://developers.google.com/apps-script/guides/html/templates#scriptlets

Adding '!'添加“!” immediately after the first '?'紧接在第一个'?' 之后will force-print the result to the page.将结果强制打印到页面。 After the DOM tree is loaded, I add the event listener that executes the 'showDialog()' function in '.gs' file when user clicks the button.加载 DOM 树后,我添加了在用户单击按钮时执行“.gs”文件中的“showDialog()”函数的事件侦听器。

The template for the dialog window is from the picker quickstart here对话框窗口的模板来自此处的选择器快速入门

https://developers.google.com/apps-script/guides/dialogs https://developers.google.com/apps-script/guides/dialogs

I modified the 'onApiLoaded' function to display the picker immediately upon loading.我修改了“onApiLoaded”函数以在加载时立即显示选择器。

 function onApiLoad() {
      gapi.load('picker', {'callback': function() {
        pickerApiLoaded = true;
        getOAuthToken();

      }});
     }

Finally, I added the following code to the 'pickerCallback' function.最后,我将以下代码添加到“pickerCallback”函数中。

if (action == google.picker.Action.PICKED) {
        var doc = data[google.picker.Response.DOCUMENTS][0];
        var id = doc[google.picker.Document.ID];
        var url = doc[google.picker.Document.URL];
        var title = doc[google.picker.Document.NAME];

        google.script.run.withSuccessHandler(function(){

           google.script.host.close();

        }).showSidebar({id: id, url: url, title: title});

When user makes a selection, the data gets passed as parameter to the 'showSideBar' function in '.gs' file.当用户进行选择时,数据将作为参数传递给“.gs”文件中的“showSideBar”函数。 Since 'google.script.host(run)' calls are asynchronous, I placed the window-closing call inside 'withSuccessHandler' to prevent early closure.由于 'google.script.host(run)' 调用是异步的,我将关闭窗口的调用放置在'withSuccessHandler' 中以防止提前关闭。

Result: Selection data is displayed in the sidebar.结果:选择数据显示在侧栏中。

There's another approach that I like better.还有另一种我更喜欢的方法。 In GAS, even file-bound scripts can be published as web apps, so you can basically create multi-page apps by making calls via UrlFetchApp.fetch().在 GAS 中,甚至文件绑定脚本也可以发布为 Web 应用程序,因此您基本上可以通过 UrlFetchApp.fetch() 进行调用来创建多页应用程序。 For example, you can define one master page containing a single 'container' div.例如,您可以定义一个包含单个“容器”div 的母版页。 You may also store all JS code and load the APIs on the master page.您还可以存储所有 JS 代码并在母版页上加载 API。 If you need to navigate to another page, you can simply query the web app via fetch(url) and paste the resulting html into container on the master page via如果您需要导航到另一个页面,您可以简单地通过 fetch(url) 查询网络应用程序,然后通过以下方式将生成的 html 粘贴到母版页上的容器中

containerDiv.innerHTML = html;

The url for the web app can be dynamically obtained by calling ScriptApp.getService().getUrl().可以通过调用 ScriptApp.getService().getUrl() 动态获取 Web 应用程序的 url。 However, the script must first be published as a web app that is anonymously accessible.但是,脚本必须首先发布为可匿名访问的 Web 应用程序。 Hope this helps.希望这可以帮助。

Just modify your picker Html code to automatically run getOAuthToken() functions when the html on modal dialog loads, like so:只需修改您的选择器 Html 代码即可在模式对话框上的 html 加载时自动运行getOAuthToken()函数,如下所示:

... Code till here remains as above
<body onload = 'getOAuthToken()'>
  <div>
    <p id='result'></p>
  </div>
  <script src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
</body>
</html>

For more details on onload event refer here有关onload事件的更多详细信息,请参阅此处

Hope that helps.希望有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM