Добавляйте интерактивные элементы пользовательского интерфейса на карточки.

На этой странице объясняется, как добавлять виджеты и элементы пользовательского интерфейса на карточки, чтобы пользователи могли взаимодействовать с вашим приложением Google Chat, например, нажимая кнопку или отправляя информацию.

Чат-приложения могут использовать следующие чат-интерфейсы для создания интерактивных карточек:

  • Сообщения , содержащие одну или несколько карточек.
  • Домашние страницы — карточка, которая появляется на вкладке «Главная» в прямых сообщениях в приложении «Чат».
  • Диалоги — карточки, которые открываются в новом окне из сообщений и домашних страниц.

Когда пользователи взаимодействуют с картами, приложения чата могут использовать полученные данные для обработки и реагирования. Подробнее см. в статье Сбор и обработка информации от пользователей Google Chat .


Используйте Card Builder для проектирования и предварительного просмотра сообщений и пользовательских интерфейсов для приложений чата:

Откройте конструктор карт

Предпосылки

Приложение Google Chat, настроенное на получение событий взаимодействия и реагирование на них. Чтобы создать интерактивное приложение Chat, выполните одно из следующих быстрых действий в зависимости от архитектуры приложения , которую вы хотите использовать:

Добавить кнопку

Виджет ButtonList отображает набор кнопок. Кнопки могут содержать текст, значок или и текст, и значок одновременно. Каждая Button поддерживает действие OnClick , которое выполняется при нажатии кнопки пользователем. Например:

  • Откройте гиперссылку с помощью OpenLink , чтобы предоставить пользователям дополнительную информацию.
  • Выполнить action , запускающее пользовательскую функцию, например вызов API.

Для удобства использования кнопки поддерживают альтернативный текст.

Добавить кнопку, которая запускает пользовательскую функцию

Ниже представлена карточка, состоящая из виджета ButtonList с двумя кнопками. Одна кнопка открывает документацию разработчика Google Chat в новой вкладке. Другая кнопка запускает пользовательскую функцию goToView() и передаёт параметр viewType="BIRD EYE VIEW" .

Добавить кнопку в стиле Material Design

Ниже показан набор кнопок в различных стилях Material Design.

Чтобы применить стиль Material Design, не включайте атрибут «цвет».

Добавить кнопку с пользовательским цветом и деактивированную кнопку

Вы можете запретить пользователям нажимать кнопку, установив "disabled": "true" .

Ниже показана карточка, состоящая из виджета ButtonList с двумя кнопками. Одна кнопка использует поле Color для настройки цвета фона. Другая кнопка деактивирована полем Disabled , что не позволяет пользователю нажать кнопку и выполнить функцию.

Добавить кнопку со значком

Ниже показана карточка, состоящая из виджета ButtonList с двумя виджетами Button . Одна кнопка использует поле knownIcon для отображения встроенного значка электронной почты Google Chat. Другая кнопка использует поле iconUrl для отображения пользовательского виджета-значка .

Добавить кнопку со значком и текстом

Ниже показана карточка, содержащая виджет ButtonList , предлагающий пользователю отправить электронное письмо. Первая кнопка отображает значок электронной почты, а вторая — текст. Пользователь может нажать на значок или кнопку с текстом, чтобы запустить функцию sendEmail .

Настройте кнопку для сворачиваемого раздела

Настройте кнопку управления, которая сворачивает и разворачивает разделы карточки. Выберите один из множества значков или изображений для визуального представления содержимого раздела, чтобы пользователям было проще понимать информацию и взаимодействовать с ней.

Добавить дополнительное меню

Overflow menu можно использовать в карточках чата для предоставления дополнительных опций и действий. Оно позволяет добавлять больше опций, не перегружая интерфейс карточки, обеспечивая чистый и организованный дизайн.

Добавить список фишек

Виджет ChipList обеспечивает универсальное и визуально привлекательное отображение информации. Используйте списки чипов для представления тегов, категорий и других релевантных данных, упрощая пользователям навигацию и взаимодействие с вашим контентом.

Сбор информации от пользователей

В этом разделе объясняется, как добавлять виджеты, собирающие информацию, например текст или выбор.

Чтобы узнать, как обрабатывать вводимые пользователями данные, ознакомьтесь со статьей Сбор и обработка информации от пользователей Google Chat .

Собрать текст

Виджет TextInput предоставляет поле для ввода текста. Виджет поддерживает подсказки, помогающие пользователям вводить унифицированные данные, а также Actions при изменении, которые выполняются при изменении поля ввода текста, например, при добавлении или удалении текста пользователем.

Если вам нужно собрать абстрактные или неизвестные данные от пользователей, используйте этот виджет TextInput . Для сбора определённых данных от пользователей используйте виджет SelectionInput .

Ниже представлена карточка, состоящая из виджета TextInput :

Собирайте даты и время

Виджет DateTimePicker позволяет пользователям вводить дату, время или и дату, и время одновременно. Кроме того, пользователи могут использовать виджет для выбора даты и времени. Если пользователь вводит неверную дату или время, виджет отображает ошибку, предлагая пользователю ввести правильную информацию.

Ниже показана карточка, состоящая из трех различных типов виджетов DateTimePicker :

Разрешить пользователям выбирать элементы

Виджет SelectionInput предоставляет набор выбираемых элементов, таких как флажки, переключатели, переключатели и раскрывающееся меню. Вы можете использовать этот виджет для сбора определённых и стандартизированных данных от пользователей. Для сбора неопределённых данных от пользователей используйте виджет TextInput .

Виджет SelectionInput поддерживает подсказки, которые помогают пользователям вводить единообразные данные, а также Actions при изменении, которые запускаются при изменении поля ввода выбора, например, когда пользователь выбирает или отменяет выбор элемента.

Чат-приложения могут получать и обрабатывать значения выбранных элементов. Подробнее о работе с данными, введенными в формы, см. в разделе Обработка информации, введённой пользователями .

В этом разделе приведены примеры карточек, использующих виджет SelectionInput . В примерах используются различные типы полей ввода:

Добавить флажок

Ниже показана карточка, предлагающая пользователю указать, является ли контакт профессиональным, личным или и тем, и другим, с виджетом SelectionInput , который использует флажки:

Добавить радиокнопку

Ниже показана карточка, предлагающая пользователю указать, является ли контакт профессиональным или личным, с помощью виджета SelectionInput , который использует переключатели:

Добавить переключатель

Ниже показана карточка, предлагающая пользователю указать, является ли контакт профессиональным, личным или и тем, и другим, с помощью виджета SelectionInput , который использует переключатели:

Ниже показана карточка, предлагающая пользователю указать, является ли контакт профессиональным или личным, с помощью виджета SelectionInput , который использует раскрывающееся меню:

Добавить меню множественного выбора

Ниже показана карточка, предлагающая пользователю выбрать контакты из меню множественного выбора:

Вы можете заполнить пункты меню с множественным выбором из следующих источников данных в Google Workspace:

  • Пользователи Google Workspace : вы можете заполнять данные только о пользователях из одной организации Google Workspace.
  • Чат-пространства : пользователь, вводящий элементы в меню множественного выбора, может просматривать и выбирать только те пространства, к которым он принадлежит в своей организации 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"
    }
  }
}

Следующий код демонстрирует меню с множественным выбором чат-пространств. Для заполнения пространств в поле выбора указывается поле 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 .

Следующий код демонстрирует меню с множественным выбором элементов из внешнего набора контактов для пользователя. Меню отображает один контакт по умолчанию и запускает функцию getContacts для извлечения и заполнения элементов из внешнего источника данных:

Node.js

узел/выбор-вход/индекс.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/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/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/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

узел/выбор-вход/индекс.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/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/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/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
              }
            ]
          }
        }
      ]
    }
  ]
}
Установите действие all_widgets_are_required в dateTimePicker

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 не отображается сообщение об ошибке, но приложение или карточка Chat выдаёт неожиданный результат; например, сообщение может не появиться.

Хотя сообщение об ошибке может не отображаться в пользовательском интерфейсе чата, при включенном ведении журнала ошибок для приложений чата доступны описательные сообщения об ошибках и данные журнала, которые помогут вам исправить ошибки. Сведения о просмотре, отладке и исправлении ошибок см. в статье «Устранение неполадок и исправление ошибок Google Chat» .