Apps Script and Drive Picker: A Love Story Written in Web Components

Google Drive Picker just got a major upgrade: . Drop it into your React, Vue, or SolidJS app with ease. But we're not stopping there. Let's see how it plays with Apps Script. Steps Project Settings Apps Script server functions HTML template and JavaScript Deploy and run Project settings (optional depends on scope) If you are only using the picker with drive or drive.readonly you can skip to the next section. The primary requirement is a Google Cloud Project which will be associated with the drive.file scope. The Cloud project number should also be set on your Apps Script project. See these instructions for details and the screenshot below. The above steps can be skipped if you are using the drive or drive.readonly steps. Apps Script server functions The Apps Script server side has two primary functions in this basic demo, doGet() and handler(event). The doGet() is a special function that can return HTML and evaluate a basic template language. function doGet() { const template = HtmlService.createTemplateFromFile("Index"); // drive or drive.file scope set in appsscript.json template.oauthToken = ScriptApp.getOAuthToken(); // Cloud project number must also be // set for the Apps Script project // if using `drive.file` scope template.appId = 246724281745; return template.evaluate(); } The above function gets the HTML content to the user, the following function listens to events sent from the client based upon user interactions with the Picker dialog. /** * Handles the Drive Picker event on the server. * * @param {CustomEvent} - event passed from `drive-picker` element * @return {CustomEvent|DriveApp.File} */ function eventHandler({ type, detail }) { console.log({ type, detail }); switch (type) { case "picker:picked": // DriveApp doesn't work with drive.file scope return Drive.Files.get(detail.docs[0].id); default: // echo back event return { type, detail }; } } HTML template and JavaScript The entire HTML file has the following contents. /** * Handles events from the drive-picker element and sends them to the server * @param {CustomEvent} event - The event object from the drive-picker. * * @see https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent */ async function eventHandler({ type, detail }) { console.log({ type, detail }); const response = await new Promise((resolve, reject) => { // Sends the event data to the server using google.script.run. // https://developers.google.com/apps-script/guides/html/communication google.script.run .withSuccessHandler(resolve) .withFailureHandler(reject) .eventHandler({ type, detail }); }); console.log({ response }); } // Initializes the drive-picker element after the DOM content is loaded. document.addEventListener("DOMContentLoaded", function () { const picker = document.createElement("drive-picker"); picker.setAttribute("oauth-token", ""); picker.setAttribute("app-id", ""); picker.setAttribute("origin", google.script.host.origin); const view = document.createElement("drive-picker-docs-view"); view.setAttribute("owned-by-me", "true"); picker.appendChild(view); // see https://github.com/googleworkspace/drive-picker-element // for more information on additional attributes and views // see https://developers.google.com/drive/picker/reference/picker // for Drive Picker reference docs for (const eventName of [ "picker:picked", "picker:canceled", "picker:error", ]) { picker.addEventListener(eventName, eventHandler); } document.body.appendChild(picker); }); This HTML renders the custom element as the following: The origin, which corresponds to google.picker.PickerBuilder.setOrigin(), is required because of the multiple iFrames and can use the client side JavaScript object, google.script.host.origin. Deploy and run When you run this app, the Picker dialog will open! The above code is implemented to pass the events to the Apps Script server using google.script.run. When running this code, the events and response from the server for the picker:picked event look like the following. { "type": "picker:picked", "detail": { "action": "picked", "viewToken": [ "all", null, { "ownedByMe": true } ], "docs": [ { "id": "OMITTED", "serviceId": "DoclistBlob", "mimeType": "application/pdf", "name": "OMITTED", "description": "", "type": "

Feb 13, 2025 - 04:18
 0
Apps Script and Drive Picker: A Love Story Written in Web Components

Google Drive Picker just got a major upgrade: . Drop it into your React, Vue, or SolidJS app with ease. But we're not stopping there. Let's see how it plays with Apps Script.

Steps

  1. Project Settings
  2. Apps Script server functions
  3. HTML template and JavaScript
  4. Deploy and run

Web Components are like tiny, polite robots. They do their job, they keep their code tidy, and they don't bother anyone else. It's the future of web development, people.

Project settings (optional depends on scope)

If you are only using the picker with drive or drive.readonly you can skip to the next section.

The primary requirement is a Google Cloud Project which will be associated with the drive.file scope. The Cloud project number should also be set on your Apps Script project. See these instructions for details and the screenshot below.

screenshot of Apps Script settings

The above steps can be skipped if you are using the drive or drive.readonly steps.

Apps Script server functions

The Apps Script server side has two primary functions in this basic demo, doGet() and handler(event). The doGet() is a special function that can return HTML and evaluate a basic template language.

function doGet() {
  const template = HtmlService.createTemplateFromFile("Index");

  // drive or drive.file scope set in appsscript.json
  template.oauthToken = ScriptApp.getOAuthToken();

  // Cloud project number must also be
  // set for the Apps Script project
  // if using `drive.file` scope
  template.appId = 246724281745;

  return template.evaluate();
}

The above function gets the HTML content to the user, the following function listens to events sent from the client based upon user interactions with the Picker dialog.

/**
 * Handles the Drive Picker event on the server.
 *
 * @param {CustomEvent} - event passed from `drive-picker` element
 * @return {CustomEvent|DriveApp.File}
 */
function eventHandler({ type, detail }) {
  console.log({ type, detail });

  switch (type) {
    case "picker:picked":
      // DriveApp doesn't work with drive.file scope
      return Drive.Files.get(detail.docs[0].id);
    default:
      // echo back event
      return { type, detail };
  }
}

HTML template and JavaScript

The entire HTML file has the following contents.



  
    
    

    
  

  

This HTML renders the custom element as the following:


  oauth-token="ya29.OMITTED"
  app-id="246724281745"
  origin="https://script.google.com"
  aria-hidden="true"
  > owned-by-me="true">
>

The origin, which corresponds to google.picker.PickerBuilder.setOrigin(), is required because of the multiple iFrames and can use the client side JavaScript object, google.script.host.origin.

Deploy and run

When you run this app, the Picker dialog will open!

Picker dialog

The above code is implemented to pass the events to the Apps Script server using google.script.run. When running this code, the events and response from the server for the picker:picked event look like the following.

{
    "type": "picker:picked",
    "detail": {
        "action": "picked",
        "viewToken": [
            "all",
            null,
            {
                "ownedByMe": true
            }
        ],
        "docs": [
            {
                "id": "OMITTED",
                "serviceId": "DoclistBlob",
                "mimeType": "application/pdf",
                "name": "OMITTED",
                "description": "",
                "type": "file",
                "lastEditedUtc": 2086003696000,
                "iconUrl": "https://drive-thirdparty.googleusercontent.com/16/type/application/pdf",
                "url": "https://drive.google.com/file/d/OMITTED/view?usp=drive_web",
                "embedUrl": "https://drive.google.com/file/d/OMITTED/preview?usp=drive_web",
                "driveSuccess": true,
                "sizeBytes": 95211,
                "parentId": "OMITTED",
                "organizationDisplayName": "Google.com"
            }
        ]
    }
}

To confirm the file access, the handler function calls the Drive Advanced Service and returns basic information about the file to the client using Drive.Files.get(detail.docs[0].id);

// response
{
    "response": {
        "name": "OMITTED",
        "mimeType": "application/pdf",
        "id": "OMITTED",
        "kind": "drive#file"
    }
}

Instead of just returning metadata, you could incorporate this into other business processes or use Gemini and VertexAI!

Checkout the element at https://github.com/googleworkspace/drive-picker-element.