インタラクティブな 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 デベロッパー向けドキュメントが新しいタブで開きます。もう 1 つのボタンは、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

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

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

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

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

ラジオボタンを追加する

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

スイッチを追加する

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

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

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

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

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

  • Google Workspace ユーザー: 同じ Google Workspace 組織内のユーザーにのみ入力できます。
  • Chat スペース: マルチ選択メニューにアイテムを入力するユーザーは、Google Workspace 組織内で自分が所属するスペースのみを表示および選択できます。

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

次のコードは、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 がトリガーされたときに、Chat アプリがアイテムを提案します。

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 アプリまたはカードで予期しない結果が生成されることがあります(カード メッセージが表示されないなど)。

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