インタラクティブな UI 要素をカードに追加する

このページでは、ユーザーがボタンをクリックしたり、情報を送信したりするなど、Google Chat アプリを操作できるように、カードにウィジェットと UI 要素を追加する方法について説明します。

Chat 用アプリは、次の Chat インターフェースを使用してインタラクティブなカードを構築できます。

  • 1 つ以上のカードを含むメッセージ
  • ホームページ。Chat 用アプリとのダイレクト メッセージの [ホーム] タブに表示されるカードです。
  • ダイアログ: メッセージやホームページから新しいウィンドウで開くカード。

ユーザーがカードを操作すると、Chat 用アプリは受け取ったデータを使用して、それに応じて処理と応答を行うことができます。詳しくは、Google Chat ユーザーから情報を収集して処理するをご覧ください。


カード作成ツールを使用して、Chat 用アプリのメッセージとユーザー インターフェースを設計してプレビューします。

カードビルダーを開く

前提条件

インタラクション イベントを受け取って応答するように構成された Google Chat 用アプリ。双方向の Chat 用アプリを作成するには、使用するアプリのアーキテクチャに基づいて、次のいずれかのクイック スタートを完了します。

ボタンを追加する

ButtonList ウィジェットには、一連のボタンが表示されます。ボタンには、テキスト、アイコン、またはテキストとアイコンの両方を表示できます。各 Button は、ユーザーがボタンをクリックしたときに発生する OnClick アクションをサポートしています。次に例を示します。

  • OpenLink を使用してハイパーリンクを開き、ユーザーに追加情報を提供します。
  • API の呼び出しなど、カスタム関数を実行する action を実行します。

ユーザー補助機能として、ボタンは代替テキストをサポートしています。

カスタム関数を実行するボタンを追加する

次の例は、2 つのボタンを含む ButtonList ウィジェットで構成されるカードです。1 つのボタンをクリックすると、Google Chat デベロッパー ドキュメントが新しいタブで開きます。もう一方のボタンは、goToView() というカスタム関数を実行し、viewType="BIRD EYE VIEW" パラメータを渡します。

マテリアル デザイン スタイルのボタンを追加する

以下は、さまざまなマテリアル デザインのボタン スタイルでボタンのセットを表示します。

マテリアル デザイン スタイルを適用するには、color 属性を含めないでください。

カスタムカラーのボタンと無効化されたボタンを追加する

"disabled": "true" を設定すると、ユーザーがボタンをクリックできないようにすることができます。

次の例は、2 つのボタンを含む ButtonList ウィジェットで構成されるカードを表示します。1 つのボタンでは、Color フィールドを使用してボタンの背景色をカスタマイズしています。もう一方のボタンは Disabled フィールドで無効化され、ユーザーがボタンをクリックして関数を実行することを防ぎます。

アイコン付きのボタンを追加する

次の例は、2 つのアイコン Button ウィジェットを含む ButtonList ウィジェットで構成されるカードを表示します。1 つのボタンは knownIcon フィールドを使用して、Google Chat の組み込みメールアイコンを表示します。もう一方のボタンは、iconUrl フィールドを使用してカスタム アイコン ウィジェットを表示します。

アイコンとテキストを含むボタンを追加する

次の例は、ユーザーにメールの送信を促す ButtonList ウィジェットで構成されたカードを表示します。1 つ目のボタンにはメールアイコンが表示され、2 つ目のボタンにはテキストが表示されます。ユーザーはアイコンまたはテキスト ボタンのいずれかをクリックして、sendEmail 関数を実行できます。

折りたたみ可能なセクションのボタンをカスタマイズする

カード内のセクションの折りたたみと展開を行うコントロール ボタンをカスタマイズします。さまざまなアイコンや画像から選択してセクションのコンテンツを視覚的に表すことで、ユーザーがより簡単に情報を理解して操作できるようになります。

オーバーフロー メニューを追加する

Overflow menu は、Chat のカードで追加のオプションやアクションを提供するために使用できます。カードのインターフェースが煩雑にならないように、すっきりと整理されたデザインを実現できます。

チップリストを追加する

ChipList ウィジェットは、さまざまな用途で情報をわかりやすく表示できる手段を提供します。チップリストを使用してタグ、カテゴリ、その他の関連データを表すことで、ユーザーがより簡単にナビゲートしてコンテンツを操作できるようになります。

ユーザーから情報を収集する

このセクションでは、テキストや選択などの情報を収集するウィジェットを追加する方法について説明します。

ユーザーが入力した内容を処理する方法については、Google Chat ユーザーから情報を収集して処理するをご覧ください。

テキストを収集する

TextInput ウィジェットは、ユーザーがテキストを入力できるフィールドを提供します。このウィジェットは、ユーザーが均一なデータを入力するのに役立つ候補と、テキスト入力フィールドで変更が発生したときに実行される Actions の変更時アクションをサポートしています。たとえば、ユーザーがテキストを追加または削除したときなどです。

ユーザーから抽象的なデータや不明なデータを収集する必要がある場合は、この TextInput ウィジェットを使用します。ユーザーから定義されたデータを収集するには、代わりに SelectionInput ウィジェットを使用します。

以下は、TextInput ウィジェットで構成されたカードです。

日付や時刻を収集する

DateTimePicker ウィジェットを使用すると、ユーザーは日付、時刻、または日付と時刻の両方を入力できます。また、ユーザーは選択ツールを使用して日付と時刻を選択することもできます。ユーザーが無効な日付や時刻を入力すると、ピッカーにエラーが表示され、ユーザーに正しい情報を入力するよう促します。

以下は、3 種類の DateTimePicker ウィジェットで構成されるカードを表示します。

ユーザーがアイテムを選択できるようにする

SelectionInput ウィジェットは、チェックボックス、ラジオボタン、スイッチ、プルダウン メニューなど、選択可能な項目のセットを提供します。このウィジェットを使用すると、ユーザーから定義済みで標準化されたデータを収集できます。ユーザーから未定義のデータを収集するには、代わりに TextInput ウィジェットを使用します。

SelectionInput ウィジェットは、ユーザーが均一なデータを入力するのに役立つ候補と、選択入力フィールドで変更が発生したときに実行される Actions である変更時アクションをサポートしています(ユーザーがアイテムを選択または選択解除した場合など)。

Chat 用アプリは、選択したアイテムの値を受信して処理できます。フォーム入力の操作について詳しくは、ユーザーが入力した情報を処理するをご覧ください。

このセクションでは、SelectionInput ウィジェットを使用するカードの例を示します。この例では、さまざまなタイプのセクション入力を使用しています。

チェックボックスを追加する

次の例は、連絡先が仕事用か個人用か、または両方かをユーザーに指定するよう求めるカードを表示します。このカードには、チェックボックスを使用する SelectionInput ウィジェットが含まれています。

ラジオボタンを追加する

次の例は、ラジオボタンを使用する SelectionInput ウィジェットで、連絡先が仕事用か個人用かを指定するようユーザーに求めるカードを表示します。

スイッチを追加する

次の例では、スイッチを使用する SelectionInput ウィジェットを使用して、連絡先が仕事用か個人用か、または両方かを指定するようユーザーに求めるカードを表示します。

次の例では、ドロップダウン メニューを使用する SelectionInput ウィジェットを使用して、連絡先が仕事用か個人用かを指定するようユーザーに求めるカードを表示します。

複数選択メニューを追加する

次のコードは、ユーザーにマルチセレクト メニューから連絡先を選択するよう求めるカードを表示します。

Google Workspace の次のデータソースから、複数選択メニューの項目を設定できます。

  • Google Workspace ユーザー: 同じ Google Workspace 組織内のユーザーのみを入力できます。
  • Chat スペース: 複数選択メニューで項目を入力するユーザーは、Google Workspace 組織内の自分が所属するスペースのみを表示して選択できます。

Google Workspace データソースを使用するには、platformDataSource フィールドを指定します。他の選択入力タイプとは異なり、これらの選択項目は Google Workspace から動的に取得されるため、SelectionItem オブジェクトは省略します。

次のコードは、Google Workspace ユーザーの複数選択メニューを示しています。ユーザーを入力するには、選択入力で commonDataSourceUSER に設定します。

JSON

{
  "selectionInput": {
    "name": "contacts",
    "type": "MULTI_SELECT",
    "label": "Selected contacts",
    "multiSelectMaxSelectedItems": 5,
    "multiSelectMinQueryLength": 1,
    "platformDataSource": {
      "commonDataSource": "USER"
    }
  }
}

次のコードは、Chat スペースの複数選択メニューを示しています。スペースを入力するには、選択入力で hostAppDataSource フィールドを指定します。マルチ選択メニューでは、defaultToCurrentSpacetrue に設定されます。これにより、現在のスペースがメニューのデフォルトの選択になります。

JSON

{
  "selectionInput": {
    "name": "spaces",
    "type": "MULTI_SELECT",
    "label": "Selected contacts",
    "multiSelectMaxSelectedItems": 3,
    "multiSelectMinQueryLength": 1,
    "platformDataSource": {
      "hostAppDataSource": {
        "chatDataSource": {
          "spaceDataSource": {
            "defaultToCurrentSpace": true
          }
        }
      }
    }
  }
}

マルチセレクト メニューには、サードパーティまたは外部のデータソースから項目を入力することもできます。たとえば、顧客管理(CRM)システムの販売見込み顧客のリストからユーザーが選択できるように、複数選択メニューを使用できます。

外部データソースを使用するには、externalDataSource フィールドを使用して、データソースからアイテムを返す関数を指定します。

外部データソースへのリクエストを減らすには、ユーザーがメニューに入力する前に、マルチセレクト メニューに表示される候補アイテムを含めることができます。たとえば、ユーザーが最近検索した連絡先を自動入力できます。外部データソースから候補アイテムを入力するには、SelectionItem オブジェクトを指定します。

次のコードは、ユーザーの外部連絡先セットのアイテムの複数選択メニューを示しています。メニューにはデフォルトで 1 つの連絡先が表示され、関数 getContacts が実行されて外部データソースから項目が取得され、入力されます。

Node.js

node/selection-input/index.js
selectionInput: {
  name: "contacts",
  type: "MULTI_SELECT",
  label: "Selected contacts",
  multiSelectMaxSelectedItems: 3,
  multiSelectMinQueryLength: 1,
  externalDataSource: { function: "getContacts" },
  // Suggested items loaded by default.
  // The list is static here but it could be dynamic.
  items: [getContact("3")]
}

Python

python/selection-input/main.py
'selectionInput': {
  'name': "contacts",
  'type': "MULTI_SELECT",
  'label': "Selected contacts",
  'multiSelectMaxSelectedItems': 3,
  'multiSelectMinQueryLength': 1,
  'externalDataSource': { 'function': "getContacts" },
  # Suggested items loaded by default.
  # The list is static here but it could be dynamic.
  'items': [get_contact("3")]
}

Java

java/selection-input/src/main/java/com/google/chat/selectionInput/App.java
.setSelectionInput(new GoogleAppsCardV1SelectionInput()
  .setName("contacts")
  .setType("MULTI_SELECT")
  .setLabel("Selected contacts")
  .setMultiSelectMaxSelectedItems(3)
  .setMultiSelectMinQueryLength(1)
  .setExternalDataSource(new GoogleAppsCardV1Action().setFunction("getContacts"))
  .setItems(List.of(getContact("3")))))))))));

Apps Script

apps-script/selection-input/selection-input.gs
selectionInput: {
  name: "contacts",
  type: "MULTI_SELECT",
  label: "Selected contacts",
  multiSelectMaxSelectedItems: 3,
  multiSelectMinQueryLength: 1,
  externalDataSource: { function: "getContacts" },
  // Suggested items loaded by default.
  // The list is static here but it could be dynamic.
  items: [getContact("3")]
}

外部データソースの場合、ユーザーが複数選択メニューで入力し始めた項目を自動補完することもできます。たとえば、米国の都市を入力するメニューでユーザーが Atl と入力し始めた場合、ユーザーが入力を完了する前に、Chat 用アプリで Atlanta を自動提案できます。最大 100 個のアイテムを自動補完できます。

項目を自動補完するには、ユーザーがマルチセレクト メニューに入力するたびに外部データソースをクエリして項目を返す関数を作成します。この関数は、次の処理を行う必要があります。

  • メニューに対するユーザー操作を表すイベント オブジェクトを渡します。
  • インタラクション イベントの invokedFunction 値が externalDataSource フィールドの関数と一致することを確認します。
  • 関数が一致したら、外部データソースから候補アイテムを返します。ユーザーが入力した内容に基づいてアイテムを提案するには、autocomplete_widget_query キーの値を取得します。この値は、ユーザーがメニューに入力した内容を表します。

次のコードは、外部データリソースの項目を自動補完します。前の例を使用すると、チャットアプリは関数 getContacts がトリガーされたタイミングに基づいてアイテムを提案します。

Node.js

node/selection-input/index.js
/**
 * Responds to a WIDGET_UPDATE event in Google Chat.
 *
 * @param {Object} event The event object from Chat API.
 * @return {Object} Response from the Chat app.
 */
function onWidgetUpdate(event) {
  if (event.common["invokedFunction"] === "getContacts") {
    const query = event.common.parameters["autocomplete_widget_query"];
    return { actionResponse: {
      type: "UPDATE_WIDGET",
      updatedWidget: { suggestions: { items: [
        // The list is static here but it could be dynamic.
        getContact("1"), getContact("2"), getContact("3"), getContact("4"), getContact("5")
      // Only return items based on the query from the user
      ].filter(e => !query || e.text.includes(query))}}
    }};
  }
}

/**
 * Generate a suggested contact given an ID.
 *
 * @param {String} id The ID of the contact to return.
 * @return {Object} The contact formatted as a suggested item for selectors.
 */
function getContact(id) {
  return {
    value: id,
    startIconUri: "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
    text: "Contact " + id
  };
}

Python

python/selection-input/main.py
def on_widget_update(event: dict) -> dict:
  """Responds to a WIDGET_UPDATE event in Google Chat."""
  if "getContacts" == event.get("common").get("invokedFunction"):
    query = event.get("common").get("parameters").get("autocomplete_widget_query")
    return { 'actionResponse': {
      'type': "UPDATE_WIDGET",
      'updatedWidget': { 'suggestions': { 'items': list(filter(lambda e: query is None or query in e["text"], [
        # The list is static here but it could be dynamic.
        get_contact("1"), get_contact("2"), get_contact("3"), get_contact("4"), get_contact("5")
      # Only return items based on the query from the user
      ]))}}
    }}


def get_contact(id: str) -> dict:
  """Generate a suggested contact given an ID."""
  return {
    'value': id,
    'startIconUri': "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
    'text': "Contact " + id
  }

Java

java/selection-input/src/main/java/com/google/chat/selectionInput/App.java
// Responds to a WIDGET_UPDATE event in Google Chat.
Message onWidgetUpdate(JsonNode event) {
  if ("getContacts".equals(event.at("/invokedFunction").asText())) {
    String query = event.at("/common/parameters/autocomplete_widget_query").asText();
    return new Message().setActionResponse(new ActionResponse()
      .setType("UPDATE_WIDGET")
      .setUpdatedWidget(new UpdatedWidget()
        .setSuggestions(new SelectionItems().setItems(List.of(
          // The list is static here but it could be dynamic.
          getContact("1"), getContact("2"), getContact("3"), getContact("4"), getContact("5")
        // Only return items based on the query from the user
        ).stream().filter(e -> query == null || e.getText().indexOf(query) > -1).toList()))));
  }
  return null;
}

// Generate a suggested contact given an ID.
GoogleAppsCardV1SelectionItem getContact(String id) {
  return new GoogleAppsCardV1SelectionItem()
    .setValue(id)
    .setStartIconUri("https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png")
    .setText("Contact " + id);
}

Apps Script

apps-script/selection-input/selection-input.gs
/**
 * Responds to a WIDGET_UPDATE event in Google Chat.
 *
 * @param {Object} event The event object from Chat API.
 * @return {Object} Response from the Chat app.
 */
function onWidgetUpdate(event) {
  if (event.common["invokedFunction"] === "getContacts") {
    const query = event.common.parameters["autocomplete_widget_query"];
    return { actionResponse: {
      type: "UPDATE_WIDGET",
      updatedWidget: { suggestions: { items: [
        // The list is static here but it could be dynamic.
        getContact("1"), getContact("2"), getContact("3"), getContact("4"), getContact("5")
      // Only return items based on the query from the user
      ].filter(e => !query || e.text.includes(query))}}
    }};
  }
}

/**
 * Generate a suggested contact given an ID.
 *
 * @param {String} id The ID of the contact to return.
 * @return {Object} The contact formatted as a suggested item for selectors.
 */
function getContact(id) {
  return {
    value: id,
    startIconUri: "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
    text: "Contact " + id
  };
}

カードに入力されたデータを検証する

このページでは、カードの action とウィジェットに入力されたデータを検証する方法について説明します。たとえば、ユーザーがテキスト入力フィールドにテキストを入力したことや、特定の文字数が入力されたことを検証できます。

アクションに必要なウィジェットを設定する

カードの action の一部として、アクションに必要なウィジェットの名前を requiredWidgets リストに追加します。

このアクションが呼び出されたときに、ここに記載されているウィジェットに値がない場合、フォーム アクションの送信はキャンセルされます。

アクションに "all_widgets_are_required": "true" が設定されている場合、カード内のすべてのウィジェットがこのアクションで必要になります。

マルチ選択で all_widgets_are_required アクションを設定する

JSON

{
  "sections": [
    {
      "header": "Select contacts",
      "widgets": [
        {
          "selectionInput": {
            "type": "MULTI_SELECT",
            "label": "Selected contacts",
            "name": "contacts",
            "multiSelectMaxSelectedItems": 3,
            "multiSelectMinQueryLength": 1,
            "onChangeAction": {
              "all_widgets_are_required": true
            },
            "items": [
              {
                "value": "contact-1",
                "startIconUri": "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
                "text": "Contact 1",
                "bottomText": "Contact one description",
                "selected": false
              },
              {
                "value": "contact-2",
                "startIconUri": "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
                "text": "Contact 2",
                "bottomText": "Contact two description",
                "selected": false
              },
              {
                "value": "contact-3",
                "startIconUri": "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
                "text": "Contact 3",
                "bottomText": "Contact three description",
                "selected": false
              },
              {
                "value": "contact-4",
                "startIconUri": "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
                "text": "Contact 4",
                "bottomText": "Contact four description",
                "selected": false
              },
              {
                "value": "contact-5",
                "startIconUri": "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
                "text": "Contact 5",
                "bottomText": "Contact five description",
                "selected": false
              }
            ]
          }
        }
      ]
    }
  ]
}
dateTimePicker で all_widgets_are_required アクションを設定する

JSON

{
  "sections": [
    {
      "widgets": [
        {
          "textParagraph": {
            "text": "A datetime picker widget with both date and time:"
          }
        },
        {
          "divider": {}
        },
        {
          "dateTimePicker": {
            "name": "date_time_picker_date_and_time",
            "label": "meeting",
            "type": "DATE_AND_TIME"
          }
        },
        {
          "textParagraph": {
            "text": "A datetime picker widget with just date:"
          }
        },
        {
          "divider": {}
        },
        {
          "dateTimePicker": {
            "name": "date_time_picker_date_only",
            "label": "Choose a date",
            "type": "DATE_ONLY",
            "onChangeAction":{
              "all_widgets_are_required": true
            }
          }
        },
        {
          "textParagraph": {
            "text": "A datetime picker widget with just time:"
          }
        },
        {
          "divider": {}
        },
        {
          "dateTimePicker": {
            "name": "date_time_picker_time_only",
            "label": "Select a time",
            "type": "TIME_ONLY"
          }
        }
      ]
    }
  ]
}
プルダウン メニューで all_widgets_are_required アクションを設定する

JSON

{
  "sections": [
    {
      "header": "Section Header",
      "collapsible": true,
      "uncollapsibleWidgetsCount": 1,
      "widgets": [
        {
          "selectionInput": {
            "name": "location",
            "label": "Select Color",
            "type": "DROPDOWN",
            "onChangeAction": {
              "all_widgets_are_required": true
            },
            "items": [
              {
                "text": "Red",
                "value": "red",
                "selected": false
              },
              {
                "text": "Green",
                "value": "green",
                "selected": false
              },
              {
                "text": "White",
                "value": "white",
                "selected": false
              },
              {
                "text": "Blue",
                "value": "blue",
                "selected": false
              },
              {
                "text": "Black",
                "value": "black",
                "selected": false
              }
            ]
          }
        }
      ]
    }
  ]
}

テキスト入力ウィジェットの検証を設定する

textInput ウィジェットの検証フィールドで、このテキスト入力ウィジェットの文字数制限と入力タイプを指定できます。

テキスト入力ウィジェットの文字数上限を設定する

JSON

{
  "sections": [
    {
      "header": "Tell us about yourself",
      "collapsible": true,
      "uncollapsibleWidgetsCount": 2,
      "widgets": [
        {
          "textInput": {
            "name": "favoriteColor",
            "label": "Favorite color",
            "type": "SINGLE_LINE",
            "validation": {"character_limit":15},
            "onChangeAction":{
              "all_widgets_are_required": true
            }
          }
        }
      ]
    }
  ]
}
テキスト入力ウィジェットの入力タイプを設定する

JSON

{
  "sections": [
    {
      "header": "Validate text inputs by input types",
      "collapsible": true,
      "uncollapsibleWidgetsCount": 2,
      "widgets": [
        {
          "textInput": {
            "name": "mailing_address",
            "label": "Please enter a valid email address",
            "type": "SINGLE_LINE",
            "validation": {
              "input_type": "EMAIL"
            },
            "onChangeAction": {
              "all_widgets_are_required": true
            }
          }
        },
        {
          "textInput": {
            "name": "validate_integer",
            "label": "Please enter a number",
              "type": "SINGLE_LINE",
            "validation": {
              "input_type": "INTEGER"
            }
          }
        },
        {
          "textInput": {
            "name": "validate_float",
            "label": "Please enter a number with a decimal",
            "type": "SINGLE_LINE",
            "validation": {
              "input_type": "FLOAT"
            }
          }
        }
      ]
    }
  ]
}

トラブルシューティング

Google Chat 用アプリまたはカードがエラーを返すと、Chat インターフェースに「エラーが発生しました」というメッセージが表示されます。または「リクエストを処理できません。」と表示されることがあります。Chat UI にエラー メッセージが表示されない場合でも、Chat 用アプリやカードで予期しない結果が生じることがあります。たとえば、カード メッセージが表示されないことがあります。

Chat UI にエラー メッセージが表示されない場合でも、Chat 用アプリのエラー ロギングが有効になっている場合は、エラーの修正に役立つ説明的なエラー メッセージとログデータを利用できます。エラーの表示、デバッグ、修正については、Google Chat のエラーのトラブルシューティングと修正をご覧ください。