กล่องโต้ตอบและแถบด้านข้างในเอกสาร Google Workspace

สคริปต์ที่เชื่อมโยงกับ Google เอกสาร, ชีต หรือฟอร์มจะแสดงองค์ประกอบอินเทอร์เฟซผู้ใช้ได้หลายประเภท เช่น การแจ้งเตือนและข้อความแจ้งที่สร้างไว้ล่วงหน้า รวมถึงกล่องโต้ตอบและแถบด้านข้างที่มีหน้าบริการ HTML ที่กําหนดเอง โดยทั่วไปแล้ว ระบบจะเปิดองค์ประกอบเหล่านี้จากรายการในเมนู (โปรดทราบว่าใน Google ฟอร์ม องค์ประกอบอินเทอร์เฟซผู้ใช้จะแสดงต่อผู้แก้ไขที่เปิดแบบฟอร์มเพื่อแก้ไขเท่านั้น ผู้ใช้ที่เปิดแบบฟอร์มเพื่อตอบจะไม่เห็นองค์ประกอบดังกล่าว)

กล่องโต้ตอบการแจ้งเตือน

การแจ้งเตือนคือกล่องโต้ตอบที่สร้างไว้ล่วงหน้าซึ่งจะเปิดขึ้นภายในเครื่องมือแก้ไข Google เอกสาร, ชีต, สไลด์ หรือแบบฟอร์ม โดยจะแสดงข้อความและปุ่ม "ตกลง" โดยคุณเลือกใส่ชื่อและปุ่มทางเลือกได้ ซึ่งคล้ายกับการเรียกใช้ window.alert() ใน JavaScript ฝั่งไคลเอ็นต์ภายในเว็บเบราว์เซอร์

การแจ้งเตือนจะระงับสคริปต์ฝั่งเซิร์ฟเวอร์ขณะที่กล่องโต้ตอบเปิดอยู่ สคริปต์จะทำงานต่อหลังจากที่ผู้ใช้ปิดกล่องโต้ตอบ แต่การเชื่อมต่อ JDBC จะหยุดอยู่ในช่วงการระงับ

ดังที่แสดงในตัวอย่างต่อไปนี้ Google เอกสาร ฟอร์ม สไลด์ และชีตทั้งหมดใช้เมธอด Ui.alert() ซึ่งมีอยู่ 3 รูปแบบ หากต้องการลบล้างปุ่ม "ตกลง" เริ่มต้น ให้ส่งค่าจากลิสต์Ui.ButtonSet เป็นอาร์กิวเมนต์ buttons หากต้องการประเมินว่าผู้ใช้คลิกปุ่มใด ให้เปรียบเทียบผลลัพธ์ของ alert() กับรายการ Ui.Button

function onOpen() {
  SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
    .createMenu("Custom Menu")
    .addItem("Show alert", "showAlert")
    .addToUi();
}

function showAlert() {
  var ui = SpreadsheetApp.getUi(); // Same variations.

  var result = ui.alert(
    "Please confirm",
    "Are you sure you want to continue?",
    ui.ButtonSet.YES_NO,
  );

  // Process the user's response.
  if (result == ui.Button.YES) {
    // User clicked "Yes".
    ui.alert("Confirmation received.");
  } else {
    // User clicked "No" or X in the title bar.
    ui.alert("Permission denied.");
  }
}

กล่องโต้ตอบข้อความแจ้ง

พรอมต์คือกล่องโต้ตอบที่สร้างไว้ล่วงหน้าซึ่งเปิดขึ้นภายในเครื่องมือแก้ไข Google เอกสาร, ชีต, สไลด์ หรือแบบฟอร์ม โดยจะแสดงข้อความ ช่องป้อนข้อความ และปุ่ม "ตกลง" โดยคุณเลือกใส่ชื่อและปุ่มอื่นๆ เพิ่มเติมได้ ซึ่งคล้ายกับการเรียกใช้ window.prompt() ใน JavaScript ฝั่งไคลเอ็นต์ภายในเว็บเบราว์เซอร์

พรอมต์จะระงับสคริปต์ฝั่งเซิร์ฟเวอร์ขณะที่กล่องโต้ตอบเปิดอยู่ สคริปต์จะทำงานต่อหลังจากที่ผู้ใช้ปิดกล่องโต้ตอบ แต่การเชื่อมต่อ JDBC จะหยุดอยู่ในช่วงการระงับ

ดังที่แสดงในตัวอย่างต่อไปนี้ Google เอกสาร ฟอร์ม สไลด์ และชีตทั้งหมดใช้เมธอด Ui.prompt() ซึ่งมีอยู่ 3 รูปแบบ หากต้องการลบล้างปุ่ม "ตกลง" เริ่มต้น ให้ส่งค่าจากคําอธิบายรายการ Ui.ButtonSet เป็นอาร์กิวเมนต์ buttons หากต้องการประเมินคําตอบของผู้ใช้ ให้บันทึกค่าที่แสดงผลสำหรับ prompt() จากนั้นเรียกใช้ PromptResponse.getResponseText() เพื่อดึงข้อมูลอินพุตของผู้ใช้ และเปรียบเทียบค่าที่แสดงผลสำหรับ PromptResponse.getSelectedButton() กับอาร์เรย์ Ui.Button

function onOpen() {
  SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
    .createMenu("Custom Menu")
    .addItem("Show prompt", "showPrompt")
    .addToUi();
}

function showPrompt() {
  var ui = SpreadsheetApp.getUi(); // Same variations.

  var result = ui.prompt(
    "Let's get to know each other!",
    "Please enter your name:",
    ui.ButtonSet.OK_CANCEL,
  );

  // Process the user's response.
  var button = result.getSelectedButton();
  var text = result.getResponseText();
  if (button == ui.Button.OK) {
    // User clicked "OK".
    ui.alert("Your name is " + text + ".");
  } else if (button == ui.Button.CANCEL) {
    // User clicked "Cancel".
    ui.alert("I didn't get your name.");
  } else if (button == ui.Button.CLOSE) {
    // User clicked X in the title bar.
    ui.alert("You closed the dialog.");
  }
}

กล่องโต้ตอบที่กําหนดเอง

กล่องโต้ตอบที่กำหนดเองจะแสดงอินเทอร์เฟซผู้ใช้บริการ HTML ภายในเครื่องมือแก้ไข Google เอกสาร, ชีต, สไลด์ หรือฟอร์ม

กล่องโต้ตอบที่กําหนดเองจะไม่ระงับสคริปต์ฝั่งเซิร์ฟเวอร์ขณะที่กล่องโต้ตอบเปิดอยู่ คอมโพเนนต์ฝั่งไคลเอ็นต์สามารถเรียกใช้สคริปต์ฝั่งเซิร์ฟเวอร์แบบไม่พร้อมกันได้โดยใช้ google.script API สําหรับอินเทอร์เฟซบริการ HTML

กล่องโต้ตอบสามารถปิดตัวเองได้โดยเรียกใช้ google.script.host.close() ในฝั่งไคลเอ็นต์ของอินเทอร์เฟซบริการ HTML อินเทอร์เฟซอื่นๆ จะปิดกล่องโต้ตอบไม่ได้ มีเพียงผู้ใช้หรือตัวกล่องโต้ตอบเองเท่านั้นที่ปิดได้

ดังที่แสดงในตัวอย่างต่อไปนี้ Google เอกสาร ฟอร์ม สไลด์ และชีตทั้งหมดใช้เมธอด Ui.showModalDialog() เพื่อเปิดกล่องโต้ตอบ

Code.gs

function onOpen() {
  SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
      .createMenu('Custom Menu')
      .addItem('Show dialog', 'showDialog')
      .addToUi();
}

function showDialog() {
  var html = HtmlService.createHtmlOutputFromFile('Page')
      .setWidth(400)
      .setHeight(300);
  SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
      .showModalDialog(html, 'My custom dialog');
}

Page.html

Hello, world! <input type="button" value="Close" onclick="google.script.host.close()" />

แถบด้านข้างที่กำหนดเอง

แถบด้านข้างจะแสดงอินเทอร์เฟซผู้ใช้บริการ HTML ภายในเครื่องมือแก้ไขของ Google เอกสาร ฟอร์ม สไลด์ และชีต

แถบด้านข้างจะไม่ระงับสคริปต์ฝั่งเซิร์ฟเวอร์ขณะที่กล่องโต้ตอบเปิดอยู่ คอมโพเนนต์ฝั่งไคลเอ็นต์สามารถเรียกสคริปต์ฝั่งเซิร์ฟเวอร์แบบไม่สอดคล้องกันโดยใช้ google.script API สําหรับอินเทอร์เฟซบริการ HTML

แถบด้านข้างสามารถปิดตัวเองได้โดยเรียกใช้ google.script.host.close() ในฝั่งไคลเอ็นต์ของอินเทอร์เฟซบริการ HTML แถบด้านข้างจะปิดไม่ได้จากอินเทอร์เฟซอื่นๆ แต่จะปิดได้เฉพาะจากผู้ใช้หรือตัวแถบด้านข้างเอง

ดังที่แสดงในตัวอย่างต่อไปนี้ Google เอกสาร ฟอร์ม สไลด์ และชีตทั้งหมดใช้เมธอด Ui.showSidebar() เพื่อเปิดแถบด้านข้าง

Code.gs

function onOpen() {
  SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
      .createMenu('Custom Menu')
      .addItem('Show sidebar', 'showSidebar')
      .addToUi();
}

function showSidebar() {
  var html = HtmlService.createHtmlOutputFromFile('Page')
      .setTitle('My custom sidebar');
  SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
      .showSidebar(html);
}

Page.html

Hello, world! <input type="button" value="Close" onclick="google.script.host.close()" />

กล่องโต้ตอบเปิดไฟล์

Google Picker เป็น JavaScript API ที่ให้ผู้ใช้เลือกหรืออัปโหลดไฟล์ใน Google ไดรฟ์ได้ ไลบรารีเครื่องมือเลือกของ Google สามารถใช้ใน HTML Service เพื่อสร้างกล่องโต้ตอบที่กําหนดเองซึ่งช่วยให้ผู้ใช้เลือกไฟล์ที่มีอยู่หรืออัปโหลดไฟล์ใหม่ จากนั้นส่งการเลือกนั้นกลับไปให้สคริปต์เพื่อใช้งานต่อไป

ข้อกำหนด

การใช้ Google Picker กับ Apps Script มีข้อกําหนดหลายประการ

  • ตั้งค่าสภาพแวดล้อมสำหรับ Google Picker

  • โปรเจ็กต์สคริปต์ของคุณต้องใช้โปรเจ็กต์ Google Cloud มาตรฐาน

  • ไฟล์ Manifest ของ Apps Script ต้องระบุขอบเขตการให้สิทธิ์ที่จําเป็นสําหรับ Google Picker API เพื่อให้ ScriptApp.getOAuthToken() แสดงโทเค็นที่ถูกต้องสําหรับ PickerBuilder.setOauthtoken()

  • คีย์ API ที่กําหนดไว้ใน PickerBuilder.setDeveloperKey() สามารถจํากัดไว้สําหรับ Apps Script ได้ ในส่วนข้อจำกัดของแอปพลิเคชัน ให้ทำตามขั้นตอนต่อไปนี้

    1. เลือกURL ที่มา HTTP (เว็บไซต์)
    2. ในส่วนการจำกัดเว็บไซต์ ให้คลิกเพิ่มรายการ
    3. คลิกผู้อ้างอิง แล้วป้อน *.google.com
    4. เพิ่มรายการอื่นและป้อน *.googleusercontent.com เป็นผู้อ้างอิง
    5. คลิกเสร็จสิ้น
  • คุณต้องโทรไปที่ PickerBuilder.setOrigin(google.script.host.origin)

ตัวอย่าง

ตัวอย่างต่อไปนี้แสดงเครื่องมือเลือกของ Google ใน Apps Script

code.gs

picker/code.gs
/**
 * Creates a custom menu in Google Sheets when the spreadsheet opens.
 */
function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu("Picker")
    .addItem("Start", "showPicker")
    .addToUi();
}

/**
 * Displays an HTML-service dialog in Google Sheets that contains client-side
 * JavaScript code for the Google Picker API.
 */
function showPicker() {
  const html = HtmlService.createHtmlOutputFromFile("dialog.html")
    .setWidth(800)
    .setHeight(600)
    .setSandboxMode(HtmlService.SandboxMode.IFRAME);
  SpreadsheetApp.getUi().showModalDialog(html, "Select a file");
}
/**
 * Checks that the file can be accessed.
 */
function getFile(fileId) {
  return Drive.Files.get(fileId, { fields: "*" });
}

/**
 * Gets the user's OAuth 2.0 access token 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. In this case, the function includes an unused call
 * to a DriveApp method to ensure that Apps Script requests access to all files
 * in the user's Drive.
 *
 * @return {string} The user's OAuth 2.0 access token.
 */
function getOAuthToken() {
  return ScriptApp.getOAuthToken();
}

dialog.html

picker/dialog.html
<!DOCTYPE html>
<html>
  <head>
    <link
      rel="stylesheet"
      href="https://ssl.gstatic.com/docs/script/css/add-ons.css"
    />
    <style>
      #result {
        display: flex;
        flex-direction: column;
        gap: 0.25em;
      }

      pre {
        font-size: x-small;
        max-height: 25vh;
        overflow-y: scroll;
        background: #eeeeee;
        padding: 1em;
        border: 1px solid #cccccc;
      }
    </style>
    <script>
      // TODO: Replace the value for DEVELOPER_KEY with the API key obtained
      // from the Google Developers Console.
      const DEVELOPER_KEY = "AIza...";
      // TODO: Replace the value for CLOUD_PROJECT_NUMBER with the project
      // number obtained from the Google Developers Console.
      const CLOUD_PROJECT_NUMBER = "1234567890";

      let pickerApiLoaded = false;
      let oauthToken;

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

      /**
       * Gets the user's OAuth 2.0 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((token) => {
            oauthToken = token;
            createPicker(token);
          })
          .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) {
        document.getElementById("result").innerHTML = "";

        if (pickerApiLoaded && token) {
          const picker = new google.picker.PickerBuilder()
            // Instruct Picker to display only spreadsheets in Drive. For other
            // views, see https://developers.google.com/picker/reference/picker.viewid
            .addView(
              new google.picker.DocsView(
                google.picker.ViewId.SPREADSHEETS
              ).setOwnedByMe(true)
            )
            // 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)
            .setAppId(CLOUD_PROJECT_NUMBER)
            .setCallback(pickerCallback)
            .setOrigin(google.script.host.origin)
            .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/reference/picker.responseobject
       *
       * @param {object} data The response object.
       */
      function pickerCallback(data) {
        const action = data[google.picker.Response.ACTION];
        if (action == google.picker.Action.PICKED) {
          handlePicked(data);
        } else if (action == google.picker.Action.CANCEL) {
          document.getElementById("result").innerHTML = "Picker canceled.";
        }
      }

      /**
       * Handles `"PICKED"` responsed from the Google Picker.
       *
       * @param {object} data The response object.
       */
      function handlePicked(data) {
        const doc = data[google.picker.Response.DOCUMENTS][0];
        const id = doc[google.picker.Document.ID];

        google.script.run
          .withSuccessHandler((driveFilesGetResponse) => {
            // Render the response from Picker and the Drive.Files.Get API.
            const resultElement = document.getElementById("result");
            resultElement.innerHTML = "";

            for (const response of [
              {
                title: "Picker response",
                content: JSON.stringify(data, null, 2),
              },
              {
                title: "Drive.Files.Get response",
                content: JSON.stringify(driveFilesGetResponse, null, 2),
              },
            ]) {
              const titleElement = document.createElement("h3");
              titleElement.appendChild(document.createTextNode(response.title));
              resultElement.appendChild(titleElement);

              const contentElement = document.createElement("pre");
              contentElement.appendChild(
                document.createTextNode(response.content)
              );
              resultElement.appendChild(contentElement);
            }
          })
          .withFailureHandler(showError)
          .getFile(data[google.picker.Response.DOCUMENTS][0].id);
      }

      /**
       * 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>
  </head>

  <body>
    <div>
      <button onclick="getOAuthToken()">Select a file</button>
      <div id="result"></div>
    </div>
    <script src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
  </body>
</html>

appsscript.json

picker/appsscript.json
{
    "timeZone": "America/Los_Angeles",
    "exceptionLogging": "STACKDRIVER",
    "runtimeVersion": "V8",
    "oauthScopes": [
      "https://www.googleapis.com/auth/script.container.ui",
      "https://www.googleapis.com/auth/drive.file"
    ],
    "dependencies": {
      "enabledAdvancedServices": [
        {
          "userSymbol": "Drive",
          "version": "v3",
          "serviceId": "drive"
        }
      ]
    }
  }