DEV Community

Cover image for Apps Script and Drive Picker: A new, easier way

Apps Script and Drive Picker: A new, easier way

It's now easier than ever to use Google Drive Picker in Apps Script with the new <drive-picker> web component, https://github.com/googleworkspace/drive-picker-element.

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

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();
}
Enter fullscreen mode Exit fullscreen mode

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 };
  }
}
Enter fullscreen mode Exit fullscreen mode

HTML template and JavaScript

The entire HTML file has the following contents.

<!DOCTYPE html>
<html>
  <head>
    <!-- 
    See available versions at 
    https://unpkg.com/@googleworkspace/drive-picker-element/ 
    -->
    <script src="https://unpkg.com/@googleworkspace/drive-picker-element@0/dist/index.iife.min.js"></script>

    <script>
      /**
       * 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", "<?= oauthToken ?>");
        picker.setAttribute("app-id", "<?= appId ?>");
        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);
      });
    </script>
  </head>

  <body></body>
</html>
Enter fullscreen mode Exit fullscreen mode

This HTML renders the custom element as the following:

<drive-picker
  oauth-token="ya29.OMITTED"
  app-id="246724281745"
  origin="https://script.google.com"
  aria-hidden="true"
  ><drive-picker-docs-view owned-by-me="true"></drive-picker-docs-view
></drive-picker>
Enter fullscreen mode Exit fullscreen mode

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"
            }
        ]
    }
}
Enter fullscreen mode Exit fullscreen mode

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"
    }
}
Enter fullscreen mode Exit fullscreen mode

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

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

Top comments (0)