สคริปต์ Google Apps สำหรับส่วนเสริม

มาขยาย Google ไดรฟ์ด้วย Apps Script เพื่อสร้างส่วนเสริมง่ายๆ ใช้ CardService สำหรับ UI ซึ่งเราจะเลือกสเปรดชีตสองสามรายการแล้วส่งต่อไปยังการ์ดถัดไปพร้อมการนำทาง คุณสามารถค้นหาบล็อกอื่นๆ ของฉันได้ใน Google Apps Scripts ที่นี่

บทนำ

สวัสดี ฉันชื่อ Nibes Khadka จาก Coding Lounge ของ Khadka ฉันเขียนบล็อกนี้เพราะโดยส่วนตัวแล้วฉันพบว่าเอกสารมีมากมายสำหรับผู้เริ่มต้น นอกจากนี้ยังหาบล็อกในสคริปต์ของ Google Apps ได้ยากอีกด้วย ดังนั้นบล็อกระดับเริ่มต้นนี้จึงถูกสร้างขึ้นเพื่อให้คุณเริ่มต้นได้ ฉันเชื่อว่าบล็อกนี้จะทำให้คุณเกือบ 20% คุณจะต้องทำโปรเจ็กต์ของคุณให้เสร็จเกือบ 80%

ข้อกำหนดเบื้องต้น

คุณจะต้องมีความรู้เกี่ยวกับ JavaScript และการเข้าถึง Google ไดรฟ์ ฉันกำลังใช้ ide สคริปต์ของแอป แต่ถ้าคุณต้องการพัฒนาในสภาพแวดล้อมท้องถิ่น คุณจะพบว่าคู่มือ "การตั้งค่า" นี้มีประโยชน์

การตั้งค่า

ไปที่ "แดชบอร์ด" และสร้างไฟล์สคริปต์ใหม่สำหรับโครงการ หลังจากนั้น เราจะต้องเตรียมโครงการของเราตามคำแนะนำด้านล่างนี้

หน้าแรก

ตาม "เอกสารประกอบ" มีหน้าแรกสองประเภทเมื่อคุณพัฒนาส่วนเสริมสำหรับไดรฟ์: ตามบริบทและไม่ใช่ตามบริบท

Non-Contextual คือการแสดงผลเริ่มต้นเมื่อไม่มีอะไรเกิดขึ้นเหมือนกับหน้าจอแรกที่แสดงหลังจากคลิกไอคอน Add-on ตามบริบทคือหน้าแรก/จอแสดงผลที่ปรากฏขึ้นเมื่อเราดำเนินการบางอย่าง เช่น การเลือกไฟล์ในไดรฟ์

เพื่อให้ฟังก์ชันสคริปต์ของแอปได้รับการเรียกในไดรฟ์ เราจะต้องกำหนดฟังก์ชันเหล่านั้นให้กับทริกเกอร์ที่เหมาะสมสำหรับส่วนเสริมของไดรฟ์ในไฟล์ manifest (appsscript.json)

ทริกเกอร์หน้าแรก

เมื่อผู้ใช้คลิกที่ไอคอน Add-on ระบบจะเรียกใช้เมธอด "drive.homepageTrigger" จากนั้นเมธอดนี้จะค้นหาฟังก์ชันแล้วเรียกใช้ฟังก์ชันที่ระบุในรายการ (appsscript.json) เพื่อดำเนินการต่อไป

รายการทริกเกอร์ที่เลือก

สำหรับทริกเกอร์ตามบริบท เราจะกำหนดฟังก์ชันในสคริปต์แอปของเราให้กับ drive.onItemSelectedTrigger ในไฟล์ Manifest

ขอบเขตคำสาบาน

เพื่อให้ส่วนเสริมของไดรฟ์ทำงานได้ ผู้ใช้จะต้องให้สิทธิ์ในการเข้าถึง รายการสิทธิ์เรียกว่า "ขอบเขต" ดูรายละเอียดเกี่ยวกับขอบเขตเฉพาะของไดรฟ์ได้ ที่นี่ เราจะต้องระบุขอบเขตในไฟล์ appsscript.json อีกครั้งเป็นรายการที่มี "oauthScopes"

หมายเหตุ: หากไฟล์ appsscript.json ของคุณถูกซ่อนอยู่ ให้ไปที่การตั้งค่า จากนั้นเลือกช่องทำเครื่องหมายแสดงไฟล์ Manifest “appsscript.json” ในตัวแก้ไข

ตรวจสอบไฟล์รายการสำหรับโครงการนี้ด้านล่าง

{
 "timeZone": "Asia/Kathmandu",
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8"
  "oauthScopes": [
    "https://www.googleapis.com/auth/spreadsheets",
    "https://www.googleapis.com/auth/script.storage",
    "https://www.googleapis.com/auth/drive",
    "https://www.googleapis.com/auth/drive.file",
    "https://www.googleapis.com/auth/drive.addons.metadata.readonly"
  ],
  "addOns": {
    "common": {
      "name": "Drive Extension with Apps Script",
      "logoUrl": "provide image URL to be used as logo",
      "layoutProperties": {
        "primaryColor": "#41f470",
        "secondaryColor": "#ab2699"
      }
    },
    "drive": {
      "homepageTrigger": {
        "runFunction": "onDriveHomePageOpen",
        "enabled": true
      },
      "onItemsSelectedTrigger": {
        "runFunction": "onDriveItemsSelected"
      }
    }
  }
}

การใช้สคริปต์ Apps เพื่อเข้าถึง Google Drive

ตอนนี้ในโฟลเดอร์โปรเจ็กต์รูทให้สร้างไฟล์สองไฟล์ การ์ด และไฟล์หลัก

การกำหนดฟังก์ชันสคริปต์ของ Apps ให้กับทริกเกอร์

หลัก

// On homepage trigger function
let onDriveHomePageOpen = () => homepageCard();
// On Item selected Trigger function
let onDriveItemsSelected = (e) => itemSelectedCard(e);

onDriveHomePageOpen และ onDriveItemsSelected เป็นสองฟังก์ชันที่เรากำหนดไว้ในไฟล์ Manifest ก่อนหน้านี้ ฟังก์ชันเหล่านี้จะเรียกใช้ฟังก์ชันอื่นๆ ซึ่งเราจะสร้างขึ้นในอีกสักครู่ หากคุณได้รับข้อผิดพลาดปรากฏขึ้นในการบันทึกไฟล์ ให้ปิดไฟล์นั้นไว้ก่อน

การออกแบบพฤติกรรมของการ์ด

มาสร้างการ์ดหน้าแรกแบบง่ายๆ เพื่อกำหนดให้กับทริกเกอร์ที่ไม่ใช่บริบทในไฟล์การ์ดกัน

สร้างการ์ดโฮมเพจ

let homepageCard = () => {
// Create a card with a header section
  let card = CardService.newCardBuilder().setHeader(CardService.newCardHeader());
// create card section 
  let section = CardService.newCardSection();
// add heading 
  let decoratedText = CardService.newDecoratedText()
    .setText("Select Files To Update");
// add text as a widget to the card section
  section.addWidget(decoratedText);
// add the section to the card 
  card.addSection(section);
// return card as build
  return card.build();
}

"การ์ด" สามารถใช้เพื่อ "สร้าง UI" สำหรับส่วนเสริมสำหรับ Google ไดรฟ์

นี่คือบล็อกสำหรับผู้เริ่มต้น ดังนั้นฉันจึงไม่ได้เน้นไปที่การออกแบบ

สร้างการ์ดที่ไม่ใช่บริบท

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

1. สร้าง UI ของการ์ดอย่างง่าย

let itemSelectedCard = (e) => {
  // Initial UI
  let card = CardService.newCardBuilder().setHeader(CardService.newCardHeader().setTitle("Select Sheets Update Master Sheet"));
  let filesSection = CardService.newCardSection()
  filesSection.setHeader("Selected Files");
  return card.build();
}

2. แสดงไฟล์ที่เลือกใน UI

let itemSelectedCard = (e) => {
  // Initial UI
  let card = CardService.newCardBuilder().setHeader(CardService.newCardHeader().setTitle("Select Sheets Update Master Sheet"));
  let filesSection = CardService.newCardSection()
  filesSection.setHeader("Selected Files");
 // New Code starts here 
// # 1
// Create a new array to hold selected files and data
  let selectedSheets = [];
// #2
// Fetch selected files data from drive through event objects
  if (e.drive.selectedItems.length > 0) {
    // Selected spreadsheets
// #3
// Among the selected items we'll be selecting only spreadsheets and push them to selected sheets
    e.drive.selectedItems.forEach(item => {
      if (item.mimeType === "application/vnd.google-apps.spreadsheet")
        selectedSheets.push(item)
    }
    );
  }
  // Create a counter to count the number of widgets added
// #4
// COunter is required to prevent error when pushing the file names into UI incase array is empty
  let widgetCounter = 0;
  for (let i = 0; i < selectedSheets.length; i++) {
    // #5
    // Create decorated text with selected files and 
    // add the decorated text to the card section
    filesSection.addWidget(CardService.newDecoratedText()
      //.setText(selectedSheets[i].title)
      .setText(e.drive.selectedItems[0].title)
    );
 // Increase widget counter per loop
 // #4
    widgetCounter += 1;
  }
  // #6
  // Add files as widgets only if widgetCounter is 1+
  //  It prevent error in case only non-spreadsheet files are selected 
  if (widgetCounter >= 1) {
    card.addSection(filesSection)
 }
  // Create Another card that has files list 
  return card.build();
}

ที่นี่ (ดูรหัสสำหรับการกำหนดหมายเลขเช่น #1)

  1. สร้างอาร์เรย์เพื่อเก็บข้อมูลในรายการที่เลือก
  2. ใช้ วัตถุเหตุการณ์ไดรฟ์ เพื่อดึงข้อมูลในไฟล์ที่เลือก
  3. ในรายการที่เลือก เราได้กรองเฉพาะสเปรดชีตโดยใช้ "mimeType"
  4. เราสร้างตัวนับเพื่อใช้เป็นเงื่อนไขในขณะที่เพิ่มไฟล์เป็นวิดเจ็ตในการ์ดเพื่อป้องกันข้อผิดพลาด
  5. สร้าง "ข้อความตกแต่ง" ซึ่งเป็น "วิดเจ็ต" ซึ่งจะเก็บชื่อไฟล์ของแต่ละไฟล์
  6. ในที่สุดก็เพิ่มส่วนไฟล์ทั้งหมดลงในตัวสร้างการ์ด

สร้างการดำเนินการด้วยปุ่ม

ในการ์ด การโต้ตอบเป็นไปได้ด้วย "การกระทำ" นอกจากนี้ ให้ตรวจดู "โค้ดตัวอย่าง" นี้ด้วย อย่าลืมเพิ่มขอบเขตที่กำหนดเพื่อขับเคลื่อนขอบเขตในไฟล์ Manifest ของคุณ

มาเพิ่มปุ่มใต้ส่วนไฟล์กัน ปุ่มนี้จะรวบรวมไฟล์ที่เลือกและส่งต่อไปยังการ์ดอื่นซึ่งเราจะสร้างในภายหลัง เพื่อให้สิ่งที่ซับซ้อนน้อยลง ฉันจะแบ่งโค้ดออกเป็นส่วนเล็กๆ

1. สร้างปุ่ม Ui ด้วยการดำเนินการ

let nxtButtonSection = CardService.newCardSection();
  let nxtButtonAction = CardService.newAction()
    .setFunctionName("handleNextButtonClick");

คุณสังเกตเห็นว่า handleNextButtonClick ได้รับการกำหนดให้เป็นฟังก์ชันที่จะทริกเกอร์เมื่อคลิกปุ่ม มันจะจัดการการนำทางและชี้ไปยังการ์ดใบถัดไป เราจะสร้างฟังก์ชันนี้ในภายหลัง

2. กำหนดพารามิเตอร์ที่จะส่งผ่าน

// We'll pass only pass ids of files to the next card so that we can fetch them there with id
// #1
  let selectedSheetsIDAsStr = selectedSheets.map(item => item.id).join();
// pass the values as params
// #2
  nxtButtonAction.setParameters({
    "nextCard": "nextCard",
    "selectedSheetsIDAsStr": selectedSheetsIDAsStr,
  });
// add button to the button set 
// #3
  let nxtButton = CardService.newTextButton().setText("Next").setOnClickAction(nxtButtonAction);
  let nxtButtonSet = CardService.newButtonSet().addButton(nxtButton);

ในการ์ด พารามิเตอร์จะต้องถูกส่งผ่านการดำเนินการด้วยเมธอด setParameters เป็นออบเจ็กต์ (#2) สิ่งสำคัญคือต้องจำไว้ว่าทั้งคีย์และค่าควรเป็นสตริง (ดังนั้น #1) สามารถเพิ่มปุ่มเป็น ชุดปุ่ม ในการ์ด(#3)

คุณสังเกตเห็นว่า nextCard ได้รับการกำหนดให้เป็นพารามิเตอร์ นั่นเป็นเพราะฟังก์ชัน handleNextButtonClick เป็นฟังก์ชันทั่วไปที่ใช้ชื่อของการ์ดเป็นพารามิเตอร์แทนการเข้ารหัสแบบฮาร์ดโค้ด วิธีนี้จะมีประสิทธิภาพมากขึ้นในระยะยาว

เพิ่มปุ่มลงในการ์ด

//  It prevent error in case only non-spreadsheet files are selected 
  if (widgetCounter >= 1) {
    card.addSection(filesSection)
    // new line
    nxtButtonSection.addWidget(nxtButtonSet);
    card.addSection(nxtButtonSection);
  }

การ์ดนำทาง

จากสิ่งที่ฉันเข้าใจ "การนำทางด้วยการ์ด" กล่าวโดยสรุปคือนำรายการการ์ดมารวมกัน การ์ดใหม่ที่จะนำทางจะถูกเพิ่มที่ด้านบนของสแต็ก ในขณะที่ดึงออกจากสแต็กเพื่อกลับไปยังการ์ดก่อนหน้า

มาสร้างไฟล์ใหม่ ฉันจะตั้งชื่อมันว่า helpers และเพิ่มคำแนะนำต่อไปนี้

ผู้ช่วยเหลือ

/* This is a greneral nav function
You use it with card action and as a response, it will supply card functions from cardsInventory */
let handleNextButtonClick = (e) => {
// #1
// Extract string nextCard to pass it as key in cards inventory obj
  let nxtCard = cardsInventory[e.commonEventObject.parameters.nextCard];
  // #2
  // Convert String into List of files selected by the user
  let selectFilesIdList = e.commonEventObject.parameters['selectedSheetsIDAsStr'].split(",");
// #3
// use actionResponse to create a navigation route to the next card
  let nxtActionResponse = CardService.newActionResponseBuilder()
    .setNavigation(CardService.newNavigation().pushCard(nxtCard(selectFilesIdList))) // #4, Passing the mastersheet with params
    .setStateChanged(true)
    .build();
  return nxtActionResponse;
}

/**
 *  Create a dictionary that
 is consist of cards for navigation with appropriate keys  
 */
var cardsInventory = {
  'nextCard': nextCard
}

ก่อนอื่นเรามาพูดถึงออบเจ็กต์ cardsInventory กันก่อน หากคุณจำได้ว่าเราส่งพารามิเตอร์ nextCard ก่อนหน้านี้เป็นสตริงในฟังก์ชัน itemSelectedCard nextCard นี้เป็นฟังก์ชันที่เราจะสร้างต่อไป แต่สิ่งนี้คือคุณไม่สามารถส่งสตริงและใช้เพื่ออ้างอิงตัวแปรได้ (ตรวจสอบ #1 ในโค้ด) ดังนั้นเราจึงสร้างพจนานุกรมที่จะจับคู่คีย์ที่เหมาะสมกับฟังก์ชันสำหรับการนำทาง

ภายในฟังก์ชัน handleNextButtonClick:

  1. แยกสตริงที่เป็นคีย์ไปยังออบเจ็กต์ cardInventory เพื่อดึงการ์ดที่ถูกต้องเพื่อเรียกใช้ เรากำลังใช้ "วัตถุความคิดเห็นเหตุการณ์" เพื่อแยกพารามิเตอร์ที่ส่งผ่านก่อนหน้านี้
  2. สตริงที่ถูกส่งผ่านเป็นรหัสไฟล์ที่เลือก เรากำลังแปลงเป็นอาร์เรย์อีกครั้ง
  3. NewActionResponseBuilder, SetNavigation, NewNavigation และ PushCard รวมกันจะถูกนำมาใช้เพื่อกำหนด "เส้นทาง" ใหม่ให้กับการ์ดที่เราเลือก
  4. ที่นี่ เรากำลังส่งรายการรหัสเป็นพารามิเตอร์

การ์ดถัดไปเพื่อนำทาง

เราจะสร้างการ์ดที่เรียบง่ายเพียงพอที่จะแสดงรายการ ID เพื่อแจ้งให้เราทราบว่าโค้ดของเราใช้งานได้

ขั้นแรก มาสร้างไฟล์ใหม่ next_card

var nextCard = function (lst) {
  let cardService = CardService.newCardBuilder().setHeader(CardService.newCardHeader().setTitle("Select Master Sheet To Update"));
  let filesSection = CardService.newCardSection();
  filesSection.setHeader("Selected Files");
  let widgetCounter = 0;
  let selectedFilesList = [...lst];
  selectedFilesList.forEach(id => {
    filesSection.addWidget(CardService.newDecoratedText()
      .setText(id));
    widgetCounter += 1;
  });
  if (widgetCounter >= 1) {
    cardService.addSection(filesSection);
  }

  return cardService.build();
}

สิ่งเดียวที่ควรสังเกตที่นี่คือฉันไม่ได้ใช้ไวยากรณ์ es6 เพื่อประกาศฟังก์ชัน นั่นเป็นเพราะการใช้ทำให้เกิดปัญหาการกำหนดขอบเขตและข้อผิดพลาด ฟังก์ชันไม่ได้ถูกกำหนดไว้ ดังนั้นฉันจึงไปโรงเรียนเก่าด้วย var

เผยแพร่ส่วนเสริมใน GCP เพื่อการทดสอบ

หากต้องการเผยแพร่ส่วนเสริมไปยัง GCP ให้ทำตามวิธีการ 2 ประการนี้ที่นี่

  1. สร้าง "โครงการ GCP" มาตรฐาน
  2. รวมโครงการเข้ากับ "โครงการสคริปต์แอป"

รหัสสุดท้าย

การ์ด

let itemSelectedCard = (e) => {
  // Initial UI
  let card = CardService.newCardBuilder().setHeader(CardService.newCardHeader().setTitle("Select Sheets Update Master Sheet"));
  let filesSection = CardService.newCardSection()
  filesSection.setHeader("Selected Files");
  let nxtButtonSection = CardService.newCardSection();
  let nxtButtonAction = CardService.newAction()
    .setFunctionName("handleNextButtonClick");
  let selectedSheets = [];
  if (e.drive.selectedItems.length > 0) {
    // hostApp,clientPlatform,drive,commonEventObject
    // Selected spreadsheets
    e.drive.selectedItems.forEach(item => {
      if (item.mimeType === "application/vnd.google-apps.spreadsheet")
        selectedSheets.push(item)
    }
    );
  }
  // Create a counter to count number of widgets added
  let widgetCounter = 0;
  for (let i = 0; i < selectedSheets.length; i++) {
    // Create decorated text with selected files and 
    // add the decorated text to card section
    filesSection.addWidget(CardService.newDecoratedText()
      //.setText(selectedSheets[i].title)
      .setText(e.drive.selectedItems[0].title)
    );
    widgetCounter += 1;
  }

  // Change list of  selected sheet's id  as string to pass to next card 
  let selectedSheetsIDAsStr = selectedSheets.map(item => item.id).join();
  nxtButtonAction.setParameters({
    "nextCard": "nextCard",
    "selectedSheetsIDAsStr": selectedSheetsIDAsStr,
  });
  let nxtButton = CardService.newTextButton().setText("Next").setOnClickAction(nxtButtonAction);
  let nxtButtonSet = CardService.newButtonSet().addButton(nxtButton);

  // Add files and button section only if the widgets are present
  //  It prevent error in case only non-spreadsheet files are selected 
  if (widgetCounter >= 1) {
    card.addSection(filesSection)
    nxtButtonSection.addWidget(nxtButtonSet);
    card.addSection(nxtButtonSection);
  }
  // Create Another card that has files list 
  return card.build();
}

ผู้ช่วยเหลือ

/* THis is a greneral nav function
You use it with card action and as reponse it will supply card functions from cardsInventory */
let handleNextButtonClick = (e) => {
  let nextCard = cardsInventory[e.commonEventObject.parameters.nextCard];
  console.log(nextCard)
  // Convert String into List
  let selectFilesIdList = e.commonEventObject.parameters['selectedSheetsIDAsStr'].split(",");
  let nxtActionResponse = CardService.newActionResponseBuilder()
    .setNavigation(CardService.newNavigation().pushCard(nextCard(selectFilesIdList)))
    .setStateChanged(true)
    .build();
  return nxtActionResponse;
}

/**
 *  Create a dictionary that
 is consist of cards for navigation with appropriate keys  
 */
var cardsInventory = {
  'nextCard': nextCard
}

สรุป

เอาล่ะ เรามานึกถึงสิ่งที่เราทำในโครงการกันดีกว่า

  1. กำหนดไฟล์ appscrits.json ด้วยขอบเขตและทริกเกอร์ที่เหมาะสมซึ่งจำเป็นสำหรับส่วนเสริมของไดรฟ์
  2. สร้าง UI ของการ์ดอย่างง่ายเพื่อโต้ตอบกับผู้ใช้
  3. ดึงไฟล์ที่เลือกจากไดรฟ์ด้วยสคริปต์แอพ
  4. ใช้การดำเนินการและชุดปุ่มเพื่อให้ผู้ใช้โต้ตอบกับ UI การ์ดของเรา
  5. สร้างตรรกะการนำทางอย่างง่ายเพื่อย้ายไปมาระหว่างการ์ดสองใบ

แสดงการสนับสนุนบางส่วน

นี่คือ Nibesh Khadka จาก Coding Lounge ของ Khadka ค้นหาบล็อกอื่นๆ ของฉันใน Google Apps Scripts ที่นี่ ฉันเป็นเจ้าของ Khadka’s Coding Lounge เราสร้างเว็บไซต์ แอปพลิเคชันมือถือ โปรแกรมเสริมของ Google และบล็อกเทคโนโลยีอันทรงคุณค่า จ้างเรา! กดไลค์ แบ่งปัน และสมัครรับจดหมายข่าวของเรา