ほとんどのカードベースのアドオンは、アドオン インターフェースのさまざまな「ページ」を表す複数のカードを使用して構築されます。効果的なユーザー エクスペリエンスを実現するには、アドオン内のカード間のシンプルで自然なナビゲーションを使用する必要があります。
もともと Gmail のアドオンでは、UI の異なるカード間の遷移は、Gmail によって表示されるスタックの一番上のカードを使用して、1 つのカードスタックとの間でカードをプッシュおよびポップすることで処理されます。
Google Workspace アドオンでは、ホームページとコンテキスト以外のカードが導入されます。コンテキスト カードとコンテキスト以外のカードに対応するため、Google Workspace アドオンにはそれぞれにカードカード スタックがあります。ホストでアドオンを開くと、対応する homepageTrigger
が呼び出され、スタック上に最初のホームページ カードが作成されます(次の図の濃い青色の「ホームページ」カード)。homepageTrigger
が定義されていない場合、デフォルト カードが作成され、表示され、コンテキスト以外のスタックに push されます。この最初のカードはルートカードです。
アドオンを介してユーザーが移動することで、コンテキスト以外の追加のカードがスタック(図の青色の「プッシュされたカード」)にプッシュされます。アドオン UI はスタックの一番上のカードを表示するため、新しいカードをスタックにプッシュするとディスプレイが変更され、スタックからカードをポップすると、ディスプレイは前のカードに戻ります。
アドオンにコンテキスト トリガーが定義されている場合、ユーザーがそのコンテキストに入るとトリガーが発動します。トリガー関数はコンテキスト カードを作成しますが、UI の表示は新しいカードの DisplayStyle
に基づいて更新されます。
DisplayStyle
がREPLACE
(デフォルト)の場合、現在表示されているカードがコンテキスト カード(図の濃いオレンジ色の「コンテキスト」カード)に置き換わります。これにより、コンテキスト以外のカードスタックの上に新しいコンテキスト カード スタックが実際に開始されます。このコンテキスト カードは、コンテキスト スタックのルートカードです。DisplayStyle
がPEEK
の場合、代わりに UI は、アドオン サイドバーの下部に現在のカードをオーバーレイするピークヘッダーを作成します。ピークヘッダーには新しいカードのタイトルが表示され、新しいボタンを表示するかどうかを決定するユーザーボタン コントロールが提供されます。ユーザーが [表示] ボタンをクリックすると、現在のカードが上記のREPLACE
に置き換えられます。
追加のコンテキスト カードを作成してスタックにプッシュできます(図の黄色い「プッシュされたカード」)。カードスタックを更新すると、アドオン UI が変更され、最上位のカードが表示されます。ユーザーがコンテキストを終了すると、スタック上のコンテキスト カードが削除され、最上位にコンテキストのない最上位のカードまたはホームページが表示されます。
アドオンでコンテキスト トリガーが定義されていないコンテキストをユーザーが入力すると、新しいカードは作成されず、現在のカードが引き続き表示されます。
以下で説明する Navigation
アクションは、同じコンテキストのカードに対してのみ機能します。たとえば、コンテキスト カード内の popToRoot()
は他のすべてのコンテキスト カードのみをポップし、ホームページ カードには影響しません。
一方、
ボタンを使用して、コンテキスト カードからコンテキスト以外のカードに移動できます。ナビゲーション方法
カードスタックにカードを追加または削除することで、カード間の遷移を作成できます。Navigation
クラスは、スタックからカードをプッシュおよびポップする関数を提供します。効果的なカード ナビゲーションを作成するには、ナビゲーション アクションを使用するようにウィジェットを構成します。複数のカードを同時に push またはポップすることもできますが、アドオンの開始時にスタックに最初に push される最初のホームページ カードを削除することはできません。
ユーザーによるウィジェットの操作に応じて新しいカードに移動する手順は次のとおりです。
Action
オブジェクトを作成し、定義したコールバック関数に関連付けます。- ウィジェットの適切なウィジェット ハンドラ関数を呼び出して、そのウィジェットに
Action
を設定します。 - ナビゲーションを行うコールバック関数を実装します。この関数には引数としてアクション イベント オブジェクトを指定します。以下の処理を行う必要があります。
Navigation
オブジェクトを作成して、カードの変更を定義します。1 つのNavigation
オブジェクトに複数のナビゲーション ステップを含めることができます。これらのステップは、オブジェクトに追加された順に行われます。ActionResponseBuilder
クラスとNavigation
オブジェクトを使用して、ActionResponse
オブジェクトを作成します。- ビルドされた
ActionResponse
を返します。
ナビゲーション コントロールを作成する際は、次の Navigation
オブジェクト関数を使用します。
機能 | 説明 |
---|---|
Navigation.pushCard(Card) |
現在のスタックにカードを push します。まず、カードを完全に構築する必要があります。 |
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 トリガー関数を再実行することで、ユーザーがカードを更新できるようにします。この更新は、アドオン メニュー項目からトリガーされます。
このアクションは、アドオンのマニフェスト ファイル(コンテキスト カード スタックと非コンテキスト カード スタックの「ルート」)で指定されているように、homepageTrigger
または contextualTrigger
トリガー関数によって生成されたカードに自動的に追加されます。
複数のカードを返す
ホームページまたはコンテキストのトリガー関数は、単一の Card
オブジェクト、またはアプリ UI が表示する Card
オブジェクトの配列を構築して返すために使用されます。
カードが 1 つしかない場合、そのカードはコンテキスト カード以外のコンテキスト スタックに追加され、ホストアプリの UI に表示されます。
返された配列にビルド済みの Card
オブジェクトが複数ある場合、ホストカードは、各カードのヘッダーのリストを含む新しいカードを表示します。ユーザーがいずれかのヘッダーをクリックすると、対応するカードが UI に表示されます。
ユーザーがリストからカードを選択すると、そのカードが現在のスタックにプッシュされ、ホストアプリによってカードが表示されます。
ボタンを押すと、カードヘッダー リストに戻ります。この「フラット」なカード配置は、アドオンで作成するカード間の遷移が必要ない場合は、うまく機能します。ただし、ほとんどの場合、カードの遷移を直接定義し、ホームページとコンテキストのトリガー関数で 1 つのカード オブジェクトを返すことをおすすめします。
例
複数のカード間を移動するナビゲーション カードの例を以下に示します。これらのカードは、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();
}