インタラクティブな 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 ウィジェットを含むカードを示しています。最初のボタンにはメールアイコンが表示され、2 つ目のボタンにはテキストが表示されます。ユーザーは、アイコンまたはテキスト ボタンをクリックして sendEmail 関数を実行できます。

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

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

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

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

チップリストを追加する

ChipList ウィジェットは、情報を表示するための汎用性と視覚的な魅力を備えています。チップリストを使用してタグ、カテゴリ、その他の関連データを表すことで、ユーザーがコンテンツを簡単に操作して操作できるようにします。

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

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

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

テキストを収集する

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

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

TextInput ウィジェットで構成されるカードの例を次に示します。

日時を収集する

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

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

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

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

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

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

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

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

次のコードは、連絡先が仕事用、個人用、またはその両方であるかどうかをユーザーに尋ねるカードを表示します。チェックボックスを使用する SelectionInput ウィジェットがあります。

ラジオボタンを追加する

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

スイッチを追加する

次は、スイッチを使用する SelectionInput ウィジェットで、連絡先が仕事用、個人用、またはその両方であるかどうかをユーザーに尋ねるカードを表示します。

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

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

次に、複数選択メニューから連絡先を選択するようユーザーに求めるカードを表示します。

マルチ選択メニューの項目は、Google Workspace の次のデータソースから入力できます。

  • Google Workspace ユーザー: 同じ Google Workspace 組織内のユーザーのみを入力できます。
  • チャット スペース: 複数選択メニューにアイテムを入力するユーザーは、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 キーの値を取得します。この値は、ユーザーがメニューに入力した内容を表します。

次のコードは、外部データリソースのアイテムを自動的に入力します。前述の例では、Chat アプリは関数 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 エラーのトラブルシューティングと修正をご覧ください。