インタラクティブな 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 である変更時アクションをサポートしています。

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

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

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

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

ラジオボタンを追加する

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

スイッチを追加する

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

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

プルダウン メニューに動的に入力する

Google Chat アプリで利用できます。

プルダウン メニューの項目は、Google Workspace のデータソースまたは外部データソースから動的に入力できます。動的データソースを使用するには、dataSourceConfigs フィールドを指定します。これは DataSourceConfig オブジェクトの配列です。各 DataSourceConfig には、platformDataSource または remoteDataSource を含めることができます。現時点では、1 つの DataSourceConfig のみがサポートされています。

Google Workspace のアイテムを読み込む

Google Workspace ユーザーなどの Google Workspace データソースからアイテムを読み込むには、DataSourceConfig 内の platformDataSource フィールドを指定します。静的 items を使用する場合とは異なり、SelectionItem オブジェクトは省略します。これらの選択項目は Google Workspace から動的に取得されるためです。

次のコードは、Google Workspace ユーザーを入力するプルダウン メニューを示しています。

JSON

{
  "sections": [
    {
      "header": "Section Header",
      "widgets": [
        {
          "selectionInput": {
            "name": "contacts",
            "type": "DROPDOWN",
            "label": "Select contact from organization",
            "dataSourceConfigs": [
              {
                "platformDataSource": {
                  "commonDataSource": "USER"
                },
                "minCharactersTrigger": 1
              }
            ]
          }
        }
      ]
    }
  ]
}
外部データソースからアイテムを入力する

顧客関係管理(CRM)システムなどのサードパーティまたは外部のデータソースからアイテムを入力するには、DataSourceConfig 内の remoteDataSource フィールドを使用して、データソースからアイテムを返す関数を指定します。

次のコードは、関数 getCrmLeads を実行して、外部の連絡先セットからアイテムを読み込むプルダウン メニューを示しています。

JSON

{
  "sections": [
    {
      "header": "Section Header",
      "widgets": [
        {
          "selectionInput": {
            "name": "crm_leads",
            "type": "DROPDOWN",
            "label": "Select CRM Lead",
            "dataSourceConfigs": [
              {
                "remoteDataSource": {
                  "function": "getCrmLeads"
                },
                "minCharactersTrigger": 2
              }
            ],
            "items": [
              {
                "text": "Suggested Lead 1",
                "value": "lead-1"
              }
            ]
          }
        }
      ]
    }
  ]
}

動的データソースへのリクエストを減らすには、ユーザーが入力する前にプルダウン メニューに表示される候補アイテムを含めることができます。DataSourceConfig 内で minCharactersTrigger を設定することで、ユーザーが入力した内容に基づいてアイテムを予測入力するようにプルダウンを構成することもできます。ユーザーが minCharactersTrigger で指定された文字数以上を入力すると、remoteDataSource で指定された関数がトリガーされます。関数に渡されるイベント オブジェクトには、autocomplete_widget_query キーにユーザーの入力が含まれています。

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

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

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

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

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

次のコードは、Google Workspace ユーザーの複数選択メニューを示しています。ユーザーを入力するために、選択インプット セットで commonDataSource を USER に設定します。

JSON

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

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

JSON

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

複数選択メニューでは、サードパーティまたは外部のデータソースからアイテムを入力することもできます。たとえば、複数選択メニューを使用すると、ユーザーが顧客管理(CRM)システムの見込み顧客のリストから選択できます。

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

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

次のコードサンプルは、外部データソースからアイテムをクエリして入力する複数選択メニューを示しています。

Node.js

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

FUNCTION_URL は、外部データソースをクエリする HTTP エンドポイントに置き換えます。

Python

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

FUNCTION_URL は、外部データソースをクエリする HTTP エンドポイントに置き換えます。

Java

java/chat/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(FUNCTION_URL))
  // Suggested items loaded by default.
  // The list is static here but it could be dynamic.
  .setItems(List.of(getSuggestedContact("3")))))))))));

FUNCTION_URL は、外部データソースをクエリする HTTP エンドポイントに置き換えます。

Apps Script

この例では、カード JSON を返すことでカード メッセージを送信します。Apps Script カードサービスを使用することもできます。

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

動的データソースから候補アイテムを入力する

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

提案されたアイテムを返すには、外部データソースをクエリする関数で次のことを行う必要があります。

  1. ユーザーがメニューに入力したときに Chat アプリが受信するイベント オブジェクトを処理します。
  2. イベント オブジェクトから、ユーザーが入力した値(event.commonEventObject.parameters["autocomplete_widget_query"] フィールドに表示される値)を取得します。
  3. ユーザー入力値を使用してデータソースにクエリを実行し、ユーザーに提案する 1 つ以上の SelectionItems を取得します。
  4. modifyCard オブジェクトを含むアクション RenderActions を返すことで、おすすめのアイテムを返します。

次のコードサンプルは、Chat アプリがカードの複数選択メニューでアイテムを動的に提案する方法を示しています。ユーザーがメニューに入力すると、ウィジェットの externalDataSource フィールドで指定された関数またはエンドポイントが外部データソースにクエリを実行し、ユーザーが選択できる項目を提案します。

Node.js

node/chat/selection-input/index.js
/**
 * Web app that responds to events sent from a Google Chat space.
 *
 * @param {Object} req Request sent from Google Chat space
 * @param {Object} res Response to send back
 */
app.post('/', async (req, res) => {
  // Stores the Google Chat event
  const chatEvent = req.body.chat;

  // Handle user interaction with multiselect.
  if(chatEvent.widgetUpdatedPayload) {
    return res.json(queryContacts(req.body));
  }

  // Replies with a card that contains the multiselect menu.
  return res.json({ hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
    cardsV2: [{
      cardId: "contactSelector",
      card: { sections:[{ widgets: [{
        selectionInput: {
          name: "contacts",
          type: "MULTI_SELECT",
          label: "Selected contacts",
          multiSelectMaxSelectedItems: 3,
          multiSelectMinQueryLength: 1,
          externalDataSource: { function: FUNCTION_URL },
          // Suggested items loaded by default.
          // The list is static here but it could be dynamic.
          items: [getSuggestedContact("3")]
        }
      }]}]}
    }]
  }}}}});
});

/**
 * Get contact suggestions based on text typed by users.
 *
 * @param {Object} event the event object that contains the user's query
 * @return {Object} suggestions
 */
function queryContacts(event) {
  const query = event.commonEventObject.parameters["autocomplete_widget_query"];
  return { action: { modifyOperations: [{ updateWidget: { selectionInputWidgetSuggestions: { suggestions: [
    // The list is static here but it could be dynamic.
    getSuggestedContact("1"), getSuggestedContact("2"), getSuggestedContact("3"), getSuggestedContact("4"), getSuggestedContact("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 selection item in the menu.
 */
function getSuggestedContact(id) {
  return {
    value: id,
    startIconUri: "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
    text: "Contact " + id
  };
}

FUNCTION_URL は、外部データソースをクエリする HTTP エンドポイントに置き換えます。

Python

python/chat/selection-input/main.py
@app.route('/', methods=['POST'])
def post() -> Mapping[str, Any]:
  """Handle requests from Google Chat

  Returns:
      Mapping[str, Any]: The response
  """
  # Stores the Google Chat event
  chatEvent = request.get_json().get('chat')

  # Handle user interaction with multiselect.
  if chatEvent.get('widgetUpdatedPayload') is not None:
    return json.jsonify(query_contacts(request.get_json()))

  # Replies with a card that contains the multiselect menu.
  return json.jsonify({ 'hostAppDataAction': { 'chatDataAction': { 'createMessageAction': {
    'message': { 'cardsV2': [{
      'cardId': "contactSelector",
      'card': { 'sections':[{ 'widgets': [{
        'selectionInput': {
          'name': "contacts",
          'type': "MULTI_SELECT",
          'label': "Selected contacts",
          'multiSelectMaxSelectedItems': 3,
          'multiSelectMinQueryLength': 1,
          'externalDataSource': { 'function': FUNCTION_URL },
          # Suggested items loaded by default.
          # The list is static here but it could be dynamic.
          'items': [get_suggested_contact("3")]
        }
      }]}]}
    }]}
  }}}})


def query_contacts(event: dict) -> dict:
  """Get contact suggestions based on text typed by users.

  Args:
      event (Mapping[str, Any]): The event object that contains the user's query

  Returns:
      Mapping[str, Any]: The response with contact suggestions.
  """
  query = event.get("commonEventObject").get("parameters").get("autocomplete_widget_query")
  return { 'action': { 'modifyOperations': [{ 'updateWidget': { 'selectionInputWidgetSuggestions': { 'suggestions': list(
    filter(lambda e: query is None or query in e["text"], [
      # The list is static here but it could be dynamic.
      get_suggested_contact("1"), get_suggested_contact("2"), get_suggested_contact("3"), get_suggested_contact("4"), get_suggested_contact("5")
    # Only return items based on the query from the user
    ])
  )}}}]}}


def get_suggested_contact(id: str) -> dict:
  """Generate a suggested contact given an ID.

  Args:
      id (str): The ID of the contact to return.

  Returns:
      Mapping[str, Any]: The contact formatted as a selection item in the menu.
  """
  return {
    'value': id,
    'startIconUri': "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
    'text': "Contact " + id
  }

FUNCTION_URL は、外部データソースをクエリする HTTP エンドポイントに置き換えます。

Java

java/chat/selection-input/src/main/java/com/google/chat/selectionInput/App.java
@SpringBootApplication
@RestController
// Web app that responds to events sent from a Google Chat space.
public class App {
  private static final String FUNCTION_URL = "your-function-url";

  public static void main(String[] args) {
    SpringApplication.run(App.class, args);
  }

  /**
   * Handle requests from Google Chat
   * 
   * @param event the event object sent by Google Chat
   * @return The response to be sent back to Google Chat
   */
  @PostMapping("/")
  @ResponseBody
  public GenericJson onEvent(@RequestBody JsonNode event) throws Exception {
    // Stores the Google Chat event
    JsonNode chatEvent = event.at("/chat");

    // Handle user interaction with multiselect.
    if (!chatEvent.at("/widgetUpdatedPayload").isEmpty()) {
      return queryContacts(event);
    }

    // Replies with a card that contains the multiselect menu.
    Message message = new Message().setCardsV2(List.of(new CardWithId()
      .setCardId("contactSelector")
      .setCard(new GoogleAppsCardV1Card()
        .setSections(List.of(new GoogleAppsCardV1Section().setWidgets(List.of(new GoogleAppsCardV1Widget()
          .setSelectionInput(new GoogleAppsCardV1SelectionInput()
            .setName("contacts")
            .setType("MULTI_SELECT")
            .setLabel("Selected contacts")
            .setMultiSelectMaxSelectedItems(3)
            .setMultiSelectMinQueryLength(1)
            .setExternalDataSource(new GoogleAppsCardV1Action().setFunction(FUNCTION_URL))
            // Suggested items loaded by default.
            // The list is static here but it could be dynamic.
            .setItems(List.of(getSuggestedContact("3")))))))))));

    return new GenericJson() {{
      put("hostAppDataAction", new GenericJson() {{
        put("chatDataAction", new GenericJson() {{
          put("createMessageAction", new GenericJson() {{
            put("message", message);
          }});
        }});
      }});
    }};
  }

  /**
   * Get contact suggestions based on text typed by users.
   *
   * @param event the event object that contains the user's query.
   * @return The response with contact suggestions.
   */
  GenericJson queryContacts(JsonNode event) throws Exception {
    String query = event.at("/commonEventObject/parameters/autocomplete_widget_query").asText();
    List<GoogleAppsCardV1SelectionItem> suggestions = List.of(
      // The list is static here but it could be dynamic.
      getSuggestedContact("1"), getSuggestedContact("2"), getSuggestedContact("3"), getSuggestedContact("4"), getSuggestedContact("5")
    // Only return items based on the query from the user
    ).stream().filter(e -> query == null || e.getText().indexOf(query) > -1).toList();

    return new GenericJson() {{
      put("action", new GenericJson() {{
        put("modifyOperations", List.of(new GenericJson() {{
          put("updateWidget", new GenericJson() {{
            put("selectionInputWidgetSuggestions", new GenericJson() {{
              put("suggestions", suggestions);
            }});
          }});
        }}));
      }});
    }};
  }

  /**
   * Generate a suggested contact given an ID.
   * 
   * @param id The ID of the contact to return.
   * @return The contact formatted as a selection item in the menu.
   */
  GoogleAppsCardV1SelectionItem getSuggestedContact(String id) {
    return new GoogleAppsCardV1SelectionItem()
      .setValue(id)
      .setStartIconUri("https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png")
      .setText("Contact " + id);
  }
}

FUNCTION_URL は、外部データソースをクエリする HTTP エンドポイントに置き換えます。

Apps Script

この例では、カード JSON を返すことでカード メッセージを送信します。Apps Script カードサービスを使用することもできます。

apps-script/chat/selection-input/selection-input.gs
/**
* Responds to a Message trigger in Google Chat.
*
* @param {Object} event the event object from Google Chat
* @return {Object} Response from the Chat app.
*/
function onMessage(event) {
  // Replies with a card that contains the multiselect menu.
  return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
    cardsV2: [{
      cardId: "contactSelector",
      card: { sections:[{ widgets: [{
        selectionInput: {
          name: "contacts",
          type: "MULTI_SELECT",
          label: "Selected contacts",
          multiSelectMaxSelectedItems: 3,
          multiSelectMinQueryLength: 1,
          externalDataSource: { function: "queryContacts" },
          // Suggested items loaded by default.
          // The list is static here but it could be dynamic.
          items: [getSuggestedContact("3")]
        }
      }]}]}
    }]
  }}}}};
}

/**
* Get contact suggestions based on text typed by users.
*
* @param {Object} event the event object that contains the user's query
* @return {Object} suggestions
*/
function queryContacts(event) {
  const query = event.commonEventObject.parameters["autocomplete_widget_query"];
  return { action: { modifyOperations: [{ updateWidget: { selectionInputWidgetSuggestions: { suggestions: [
    // The list is static here but it could be dynamic.
    getSuggestedContact("1"), getSuggestedContact("2"), getSuggestedContact("3"), getSuggestedContact("4"), getSuggestedContact("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 selection item in the menu.
*/
function getSuggestedContact(id) {
  return {
    value: id,
    startIconUri: "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
    text: "Contact " + id
  };
}

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

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

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

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

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

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

複数選択で allWidgetsAreRequired アクションを設定する

JSON

{
  "sections": [
    {
      "header": "Select contacts",
      "widgets": [
        {
          "selectionInput": {
            "type": "MULTI_SELECT",
            "label": "Selected contacts",
            "name": "contacts",
            "multiSelectMaxSelectedItems": 3,
            "multiSelectMinQueryLength": 1,
            "onChangeAction": {
              "allWidgetsAreRequired": 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 で allWidgetsAreRequired アクションを設定する

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":{
              "allWidgetsAreRequired": 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"
          }
        }
      ]
    }
  ]
}
プルダウン メニューで allWidgetsAreRequired アクションを設定する

JSON

{
  "sections": [
    {
      "header": "Section Header",
      "collapsible": true,
      "uncollapsibleWidgetsCount": 1,
      "widgets": [
        {
          "selectionInput": {
            "name": "location",
            "label": "Select Color",
            "type": "DROPDOWN",
            "onChangeAction": {
              "allWidgetsAreRequired": 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": {"characterLimit": 15},
            "onChangeAction":{
              "allWidgetsAreRequired": 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": {
              "inputType": "EMAIL"
            },
            "onChangeAction": {
              "allWidgetsAreRequired": true
            }
          }
        },
        {
          "textInput": {
            "name": "validate_integer",
            "label": "Please enter a number",
              "type": "SINGLE_LINE",
            "validation": {
              "inputType": "INTEGER"
            }
          }
        },
        {
          "textInput": {
            "name": "validate_float",
            "label": "Please enter a number with a decimal",
            "type": "SINGLE_LINE",
            "validation": {
              "inputType": "FLOAT"
            }
          }
        }
      ]
    }
  ]
}

トラブルシューティング

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

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

アドオンではない Chat 用アプリ: インタラクティブなカードとダイアログを設計する

以下のドキュメントは、Google Workspace アドオンではない Chat アプリに適用されます。アドオンではない Chat アプリを移行するには、Google Chat アプリを Google Workspace アドオンに変換するをご覧ください。

次のコードは、アドオンではない Chat アプリで、ユーザーの外部連絡先セットからアイテムの複数選択メニューを表示します。このメニューにはデフォルトで 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")]
}

アドオンではない Chat アプリでアイテムを予測入力するには、ユーザーが複数選択メニューに入力するたびに外部データソースにクエリを実行してアイテムを返す関数を作成します。関数は、次のことを行う必要があります。

  • メニューでのユーザー操作を表すイベント オブジェクトを渡します。
  • インタラクション イベントの 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
  };
}