การนำทางการ์ด

ส่วนเสริมแบบการ์ดส่วนใหญ่สร้างขึ้นโดยใช้การ์ดหลายรายการที่แสดงถึง "หน้า" ต่างๆ ของอินเทอร์เฟซส่วนเสริม หากต้องการมอบประสบการณ์ของผู้ใช้ที่มีประสิทธิภาพ คุณควรใช้การนำทางที่เรียบง่ายและเป็นธรรมชาติระหว่างการ์ดในส่วนเสริม

เดิมทีอยู่ในส่วนเสริมของ Gmail การเปลี่ยนระหว่างการ์ดต่างๆ ใน UI จะจัดการด้วยการพุชและเรียกการ์ดไปและกลับจากชุดการ์ดเดี่ยว โดยการ์ดบนสุดของกลุ่มการ์ดจะแสดงโดย Gmail

การไปยังส่วนต่างๆ ของการ์ดในหน้าแรก

ส่วนเสริมของ Google Workspace จะเพิ่มหน้าแรกและการ์ดที่ไม่ใช่บริบท ส่วนเสริมของ Google Workspace จะมีสแต็กการ์ดภายในสำหรับแต่ละการ์ดเพื่อรองรับการ์ดตามบริบทและการ์ดที่ไม่ใช่บริบท เมื่อเปิดส่วนเสริมในโฮสต์ homepageTrigger ที่เกี่ยวข้องจะเริ่มทำงานเพื่อสร้างการ์ดหน้าแรกรายการแรกในชุดรายการ (การ์ด "หน้าแรก" สีน้ำเงินเข้มในแผนภาพด้านล่าง) หากไม่ได้กำหนด homepageTrigger ระบบจะสร้างการ์ดเริ่มต้น แสดง และพุชไปยังสแต็กที่ไม่ใช่บริบท การ์ดแรกคือการ์ดรูท

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

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

  • หาก DisplayStyle เป็น REPLACE (ค่าเริ่มต้น) การ์ดบริบท (การ์ด "บริบท" สีส้มเข้มในแผนภาพ) จะแทนที่การ์ดที่แสดงอยู่ ซึ่งจะเป็นการเริ่มต้นสแต็กการ์ดบริบทใหม่อย่างมีประสิทธิภาพที่ด้านบนของสแต็กการ์ดที่ไม่ใช่บริบท และการ์ดบริบทนี้คือการ์ดรูทของสแต็กบริบท
  • แต่หาก DisplayStyle คือ PEEK UI จะสร้างส่วนหัวแบบโฉบเฉี่ยวซึ่งปรากฏที่ด้านล่างของแถบด้านข้างของส่วนเสริมแทนโดยวางซ้อนการ์ดปัจจุบัน ส่วนหัว peek จะแสดงชื่อของการ์ดใหม่และมีปุ่มสำหรับควบคุมให้ผู้ใช้ตัดสินใจว่าจะดูการ์ดใหม่หรือไม่ หากผู้ใช้คลิกปุ่มดู การ์ดดังกล่าวจะแทนที่การ์ดปัจจุบัน (ตามที่อธิบายไว้ข้างต้นด้วย REPLACE)

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

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

การดำเนินการ Navigation ที่อธิบายไว้ด้านล่างจะดำเนินการกับการ์ดจากบริบทเดียวกันเท่านั้น เช่น popToRoot() จากภายในการ์ดบริบทจะแสดงเฉพาะการ์ดบริบทอื่นๆ ทั้งหมดเท่านั้น และจะไม่ส่งผลต่อการ์ดในหน้าแรก

ในทางตรงกันข้าม ปุ่ม จะพร้อมให้ผู้ใช้เลื่อนดูจากการ์ดบริบทไปยังการ์ดที่ไม่ใช่บริบทเสมอ

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

หากต้องการไปยังการ์ดใหม่ตามการโต้ตอบของผู้ใช้กับวิดเจ็ต ให้ทำตามขั้นตอนต่อไปนี้

  1. สร้างออบเจ็กต์ Action และเชื่อมโยงกับฟังก์ชันเรียกกลับที่คุณกำหนด
  2. เรียกใช้ฟังก์ชันเครื่องจัดการวิดเจ็ตที่เหมาะสมของวิดเจ็ตเพื่อตั้งค่า Action ในวิดเจ็ตนั้น
  3. ใช้ฟังก์ชันเรียกกลับที่ดำเนินการนำทาง ฟังก์ชันนี้จะได้รับออบเจ็กต์เหตุการณ์การกระทำเป็นอาร์กิวเมนต์ และต้องทำดังต่อไปนี้
    1. สร้างออบเจ็กต์ Navigation เพื่อกำหนดการเปลี่ยนแปลงการ์ด ออบเจ็กต์ Navigation รายการเดียวอาจมีขั้นตอนการนำทางหลายขั้นตอน ซึ่งจะดำเนินการตามลำดับการเพิ่มในออบเจ็กต์
    2. สร้างออบเจ็กต์ ActionResponse โดยใช้คลาส ActionResponseBuilder และออบเจ็กต์ Navigation
    3. ส่งคืน ActionResponse เวอร์ชันที่สร้าง

เมื่อสร้างตัวควบคุมการไปยังส่วนต่างๆ คุณจะใช้ฟังก์ชันของออบเจ็กต์ Navigation ต่อไปนี้

การทำงาน คำอธิบาย
Navigation.pushCard(Card) พุชการ์ดไปยังสแต็กปัจจุบัน คุณจำเป็นต้องสร้างบัตรให้เสร็จสมบูรณ์ก่อน
Navigation.popCard() นำการ์ด 1 ใบออกจากด้านบนสุดของกลุ่มการ์ด ซึ่งเท่ากับการคลิกลูกศรย้อนกลับในแถวส่วนหัวของส่วนเสริม การดำเนินการนี้จะไม่นำการ์ดรูทออก
Navigation.popToRoot() นำการ์ดทั้งหมดออกจากสแต็ก ยกเว้นการ์ดรูท ซึ่งหลักๆ แล้วจะรีเซ็ตสแต็กการ์ดนั้น
Navigation.popToNamedCard(String) แสดงการ์ดจากสแต็กจนกว่าจะถึงการ์ดที่มีชื่อนั้นๆ หรือการ์ดรูทของสแต็ก คุณสามารถกำหนดชื่อให้กับการ์ดโดยใช้ฟังก์ชัน CardBuilder.setName(String)
Navigation.updateCard(Card) เปลี่ยนบัตรปัจจุบันแทนการ์ดปัจจุบันแทนบัตรปัจจุบัน โดยรีเฟรชบัตรดังกล่าวใน UI

หากการโต้ตอบหรือเหตุการณ์ของผู้ใช้ควรส่งผลให้การ์ดแสดงผลอีกครั้งในบริบทเดียวกัน ให้ใช้เมธอด Navigation.pushCard(), Navigation.popCard() และ Navigation.updateCard() เพื่อแทนที่การ์ดที่มีอยู่ หากการโต้ตอบหรือเหตุการณ์ของผู้ใช้ควรส่งผลให้การ์ดแสดงผลอีกครั้งในบริบทอื่น ให้ใช้ ActionResponseBuilder.setStateChanged() เพื่อบังคับให้มีการเรียกใช้ส่วนเสริมอีกครั้งในบริบทดังกล่าว

ต่อไปนี้เป็นตัวอย่างการนำทาง

  • หากการโต้ตอบหรือเหตุการณ์เปลี่ยนสถานะของการ์ดปัจจุบัน (เช่น การเพิ่มงานลงในรายการงาน) ให้ใช้ updateCard()
  • หากการโต้ตอบหรือกิจกรรมให้รายละเอียดเพิ่มเติมหรือแจ้งให้ผู้ใช้ดำเนินการเพิ่มเติม (เช่น คลิกที่ชื่อรายการเพื่อดูรายละเอียดเพิ่มเติม หรือกดปุ่มเพื่อสร้างกิจกรรมในปฏิทินใหม่) ให้ใช้ pushCard() เพื่อแสดงหน้าใหม่และอนุญาตให้ผู้ใช้ออกจากหน้าใหม่โดยใช้ปุ่มย้อนกลับ
  • หากการโต้ตอบหรือเหตุการณ์อัปเดตสถานะในการ์ดก่อนหน้า (เช่น อัปเดตชื่อรายการจากมุมมองรายละเอียด) ให้ใช้ popCard(), popCard(), pushCard(previous) และ pushCard(current) เพื่ออัปเดตการ์ดก่อนหน้าและการ์ดปัจจุบัน

กำลังรีเฟรชการ์ด

ส่วนเสริมของ Google Workspace ช่วยให้ผู้ใช้รีเฟรชการ์ดได้โดยเรียกใช้ฟังก์ชันทริกเกอร์ Apps Script อีกครั้งที่ลงทะเบียนไว้ในไฟล์ Manifest ผู้ใช้จะเรียกการรีเฟรชนี้ผ่านรายการในเมนูส่วนเสริม ดังนี้

แถบด้านข้างของส่วนเสริม Google Workspace

ระบบจะเพิ่มการดำเนินการนี้ลงในการ์ดที่ฟังก์ชันทริกเกอร์ homepageTrigger หรือ contextualTrigger สร้างขึ้นโดยอัตโนมัติตามที่ระบุไว้ในไฟล์ Manifest ของส่วนเสริม ("ราก" ของสแต็กการ์ดตามบริบทและที่ไม่ใช่บริบท)

การส่งคืนบัตรหลายใบ

ตัวอย่างการ์ดส่วนเสริม

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

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

หากอาร์เรย์ที่แสดงผลมีออบเจ็กต์ Card ที่สร้างขึ้นมากกว่า 1 รายการ แอปพลิเคชันโฮสต์จะแสดงการ์ดใหม่แทน ซึ่งประกอบด้วยรายการส่วนหัวของการ์ดแต่ละใบ เมื่อผู้ใช้คลิกส่วนหัวเหล่านั้น UI จะแสดงการ์ดที่เกี่ยวข้อง

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

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

ตัวอย่าง

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

  /**
   *  Create the top-level card, with buttons leading to each of three
   *  'children' cards, as well as buttons to backtrack and return to the
   *  root card of the stack.
   *  @return {Card}
   */
  function createNavigationCard() {
    // Create a button set with actions to navigate to 3 different
    // 'children' cards.
    var buttonSet = CardService.newButtonSet();
    for(var i = 1; i <= 3; i++) {
      buttonSet.addButton(createToCardButton(i));
    }

    // Build the card with all the buttons (two rows)
    var card = CardService.newCardBuilder()
        .setHeader(CardService.newCardHeader().setTitle('Navigation'))
        .addSection(CardService.newCardSection()
            .addWidget(buttonSet)
            .addWidget(buildPreviousAndRootButtonSet()));
    return card.build();
  }

  /**
   *  Create a button that navigates to the specified child card.
   *  @return {TextButton}
   */
  function createToCardButton(id) {
    var action = CardService.newAction()
        .setFunctionName('gotoChildCard')
        .setParameters({'id': id.toString()});
    var button = CardService.newTextButton()
        .setText('Card ' + id)
        .setOnClickAction(action);
    return button;
  }

  /**
   *  Create a ButtonSet with two buttons: one that backtracks to the
   *  last card and another that returns to the original (root) card.
   *  @return {ButtonSet}
   */
  function buildPreviousAndRootButtonSet() {
    var previousButton = CardService.newTextButton()
        .setText('Back')
        .setOnClickAction(CardService.newAction()
            .setFunctionName('gotoPreviousCard'));
    var toRootButton = CardService.newTextButton()
        .setText('To Root')
        .setOnClickAction(CardService.newAction()
            .setFunctionName('gotoRootCard'));

    // Return a new ButtonSet containing these two buttons.
    return CardService.newButtonSet()
        .addButton(previousButton)
        .addButton(toRootButton);
  }

  /**
   *  Create a child card, with buttons leading to each of the other
   *  child cards, and then navigate to it.
   *  @param {Object} e object containing the id of the card to build.
   *  @return {ActionResponse}
   */
  function gotoChildCard(e) {
    var id = parseInt(e.parameters.id);  // Current card ID
    var id2 = (id==3) ? 1 : id + 1;      // 2nd card ID
    var id3 = (id==1) ? 3 : id - 1;      // 3rd card ID
    var title = 'CARD ' + id;

    // Create buttons that go to the other two child cards.
    var buttonSet = CardService.newButtonSet()
      .addButton(createToCardButton(id2))
      .addButton(createToCardButton(id3));

    // Build the child card.
    var card = CardService.newCardBuilder()
        .setHeader(CardService.newCardHeader().setTitle(title))
        .addSection(CardService.newCardSection()
            .addWidget(buttonSet)
            .addWidget(buildPreviousAndRootButtonSet()))
        .build();

    // Create a Navigation object to push the card onto the stack.
    // Return a built ActionResponse that uses the navigation object.
    var nav = CardService.newNavigation().pushCard(card);
    return CardService.newActionResponseBuilder()
        .setNavigation(nav)
        .build();
  }

  /**
   *  Pop a card from the stack.
   *  @return {ActionResponse}
   */
  function gotoPreviousCard() {
    var nav = CardService.newNavigation().popCard();
    return CardService.newActionResponseBuilder()
        .setNavigation(nav)
        .build();
  }

  /**
   *  Return to the initial add-on card.
   *  @return {ActionResponse}
   */
  function gotoRootCard() {
    var nav = CardService.newNavigation().popToRoot();
    return CardService.newActionResponseBuilder()
        .setNavigation(nav)
        .build();
  }