สคริปต์ 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)
- สร้างอาร์เรย์เพื่อเก็บข้อมูลในรายการที่เลือก
- ใช้ วัตถุเหตุการณ์ไดรฟ์ เพื่อดึงข้อมูลในไฟล์ที่เลือก
- ในรายการที่เลือก เราได้กรองเฉพาะสเปรดชีตโดยใช้ "mimeType"
- เราสร้างตัวนับเพื่อใช้เป็นเงื่อนไขในขณะที่เพิ่มไฟล์เป็นวิดเจ็ตในการ์ดเพื่อป้องกันข้อผิดพลาด
- สร้าง "ข้อความตกแต่ง" ซึ่งเป็น "วิดเจ็ต" ซึ่งจะเก็บชื่อไฟล์ของแต่ละไฟล์
- ในที่สุดก็เพิ่มส่วนไฟล์ทั้งหมดลงในตัวสร้างการ์ด
สร้างการดำเนินการด้วยปุ่ม
ในการ์ด การโต้ตอบเป็นไปได้ด้วย "การกระทำ" นอกจากนี้ ให้ตรวจดู "โค้ดตัวอย่าง" นี้ด้วย อย่าลืมเพิ่มขอบเขตที่กำหนดเพื่อขับเคลื่อนขอบเขตในไฟล์ 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:
- แยกสตริงที่เป็นคีย์ไปยังออบเจ็กต์ cardInventory เพื่อดึงการ์ดที่ถูกต้องเพื่อเรียกใช้ เรากำลังใช้ "วัตถุความคิดเห็นเหตุการณ์" เพื่อแยกพารามิเตอร์ที่ส่งผ่านก่อนหน้านี้
- สตริงที่ถูกส่งผ่านเป็นรหัสไฟล์ที่เลือก เรากำลังแปลงเป็นอาร์เรย์อีกครั้ง
- NewActionResponseBuilder, SetNavigation, NewNavigation และ PushCard รวมกันจะถูกนำมาใช้เพื่อกำหนด "เส้นทาง" ใหม่ให้กับการ์ดที่เราเลือก
- ที่นี่ เรากำลังส่งรายการรหัสเป็นพารามิเตอร์
การ์ดถัดไปเพื่อนำทาง
เราจะสร้างการ์ดที่เรียบง่ายเพียงพอที่จะแสดงรายการ 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 ประการนี้ที่นี่
- สร้าง "โครงการ GCP" มาตรฐาน
- รวมโครงการเข้ากับ "โครงการสคริปต์แอป"
รหัสสุดท้าย
การ์ด
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 }
สรุป
เอาล่ะ เรามานึกถึงสิ่งที่เราทำในโครงการกันดีกว่า
- กำหนดไฟล์ appscrits.json ด้วยขอบเขตและทริกเกอร์ที่เหมาะสมซึ่งจำเป็นสำหรับส่วนเสริมของไดรฟ์
- สร้าง UI ของการ์ดอย่างง่ายเพื่อโต้ตอบกับผู้ใช้
- ดึงไฟล์ที่เลือกจากไดรฟ์ด้วยสคริปต์แอพ
- ใช้การดำเนินการและชุดปุ่มเพื่อให้ผู้ใช้โต้ตอบกับ UI การ์ดของเรา
- สร้างตรรกะการนำทางอย่างง่ายเพื่อย้ายไปมาระหว่างการ์ดสองใบ
แสดงการสนับสนุนบางส่วน
นี่คือ Nibesh Khadka จาก Coding Lounge ของ Khadka ค้นหาบล็อกอื่นๆ ของฉันใน Google Apps Scripts ที่นี่ ฉันเป็นเจ้าของ Khadka’s Coding Lounge เราสร้างเว็บไซต์ แอปพลิเคชันมือถือ โปรแกรมเสริมของ Google และบล็อกเทคโนโลยีอันทรงคุณค่า จ้างเรา! กดไลค์ แบ่งปัน และสมัครรับจดหมายข่าวของเรา