カード ナビゲーション

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

ほとんどのカードベースのアドオンは、アドオン インターフェースの異なる「ページ」を表す複数のカードを使用して作成されます。効果的なユーザー エクスペリエンスを実現するには、アドオン内のカード間のシンプルで自然なナビゲーションを使用する必要があります。

元々 Gmail のアドオンでは、UI の異なるカード間の移行は、Gmail によってスタックの一番上のカードが表示される状態で、1 つのカードスタックとの間でカードをプッシュおよびポップすることで処理されていました。

ホームページ カード ナビゲーション

Google Workspace アドオンには、ホームページとコンテキストのないカードが導入されています。コンテキスト カードと非コンテキスト カードに対応するために、Google Workspace アドオンにはそれぞれ内部カードスタックがあります。ホストでアドオンを開くと、対応する homepageTrigger が呼び出され、スタック上に最初のホームページ カード(下の図の暗い青色の「ホームページ」カード)が作成されます。homepageTrigger が定義されていない場合、デフォルト カードが作成され、表示され、コンテキストなしスタックに push されます。この最初のカードはルートカードです。

ユーザーがアドオンを操作すると、アドオンはコンテキスト以外の追加カードを作成してスタックにプッシュします(図では青いプッシュされたカード)。アドオン UI はスタック内の一番上のカードを表示するため、新しいカードをスタックに push するとディスプレイが変更されます。スタックからカードをポップすると、ディスプレイが以前のカードに戻ります。

アドオンにコンテキスト トリガーが定義されている場合は、ユーザーがそのコンテキストに入るとトリガーが起動します。トリガー関数はコンテキスト カードを作成しますが、UI の表示は新しいカードの DisplayStyle に基づいて更新されます。

  • DisplayStyleREPLACE(デフォルト)の場合、現在表示されているカードは、コンテキスト カード(図の中の暗いオレンジ色の「コンテキスト」カード)に置き換わります。これにより、非コンテキスト カード カード上に新しいコンテキスト カード スタックを効果的に開始できます。このコンテキスト カードは、コンテキスト スタックのルートカードです。
  • DisplayStylePEEK の場合、代わりに UI はアドオンのサイドバーの下部に表示されるピークヘッダーを作成して、現在のカードをオーバーレイします。ピークヘッダーには、新しいカードのタイトルと、新しいカードを表示するかどうかを決定するユーザーボタン コントロールが表示されます。[View] ボタンをクリックすると、現在のカードが置き換えられます(前述のとおり、REPLACE )。

追加のコンテキスト カードを作成して、スタックにプッシュできます(図の黄色の「プッシュされたカード」)。カードスタックを更新すると、アドオン UI が変更されて最上位カードが表示されます。ユーザーがコンテキストから離れると、スタック上のコンテキスト カードが削除され、最上位にないコンテキスト カードまたはホームページの最上位にディスプレイが更新されます。

アドオンでコンテキスト トリガーを定義していないコンテキストをユーザーが入力しても、新しいカードは作成されず、現在のカードが引き続き表示されます。

以下で説明する Navigation アクションは、同じコンテキストのカードにのみ作用します。たとえば、コンテキスト カード内からの popToRoot() のカードは、他のすべてのコンテキスト カードにのみポップされ、ホームページ カードには影響しません。

一方、 ボタンを使用すると、ユーザーはコンテキスト カードからコンテキスト外のカードに移動できます。

カードスタックからカードを追加または削除することで、カード間の移行を作成できます。Navigation クラスは、スタックからカードをプッシュおよびポップする関数を提供します。効果的なカード ナビゲーションを作成するには、ナビゲーション アクションを使用するようにウィジェットを構成します。複数のカードを同時にプッシュまたはポップできますが、アドオンの開始時に最初にスタックにプッシュされた最初のホームページ カードを削除することはできません。

ユーザーによるウィジェットの操作に応じて新しいカードに移動する手順は次のとおりです。

  1. Action オブジェクトを作成し、定義したコールバック関数に関連付けます。
  2. ウィジェットの適切なウィジェット ハンドラ関数を呼び出して、そのウィジェットに Action を設定します。
  3. ナビゲーションを行うコールバック関数を実装します。この関数には、アクション イベント オブジェクトが引数として与えられ、次の処理を行う必要があります。
    1. Navigation オブジェクトを作成して、カード変更を定義します。1 つの Navigation オブジェクトには、複数のナビゲーション ステップを含めることができます。これらのステップは、オブジェクトに追加された順序で行われます。
    2. ActionResponseBuilder クラスと Navigation オブジェクトを使用して、ActionResponse オブジェクトを作成します。
    3. ビルドされた ActionResponse を返します。

ナビゲーション コントロールを作成する場合は、次の Navigation オブジェクト関数を使用します。

Function 説明
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 トリガー関数を再実行して、カードを更新できます。ユーザーは、次の方法でアドオン メニュー項目からこの更新をトリガーできます。

 l10n-placeholder39  l10n-placeholder40





このアクションは、アドオンのマニフェスト ファイル(コンテキスト カードと非コンテキスト カード スタックの「ルート」など)で指定されているように、homepageTrigger または contextualTrigger トリガー関数によって生成されたカードに自動的に追加されます。

複数のカードの返品

アドオンカードの例

ホームページまたはコンテキスト トリガー関数は、単一の Card オブジェクト、またはアプリの UI が表示する Card オブジェクトの配列を構築して返すために使用します。

カードが 1 つしかない場合、そのカードはルートカードとして非コンテキスト スタックまたはコンテキスト スタックに追加され、ホストアプリ UI に表示されます。

返された配列に複数のビルド済み Card オブジェクトが含まれている場合、ホストアプリは新しいカードを表示します。このカードには、各カードのヘッダーのリストが含まれます。ユーザーがこれらのヘッダーのいずれかをクリックすると、対応するカードが UI に表示されます。

ユーザーがリストからカードを選択すると、そのカードは現在のスタックにプッシュされ、ホストアプリに表示されます。 ボタンをクリックすると、ユーザーはカードヘッダー リストに戻ります。

この「フラット」なカード配置は、アドオンによって作成されるカード間の移行が不要な場合に適しています。ただし、ほとんどの場合、カードの切り替えを直接定義し、ホームページとコンテキスト トリガーの関数で 1 つのカード オブジェクトを返すことをおすすめします。

次の例では、いくつかのカードを作成し、カード内にナビゲーション ボタンを配置しています。createNavigationCard() によって返されたカードを特定のコンテキストの内外に push することにより、これらのカードをコンテキスト スタックまたは非コンテキスト スタックに追加できます。

  /**
   *  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();
  }