Как добавить на карточки интерактивные элементы интерфейса

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

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

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

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


Используйте конструктор карточек, чтобы создавать и просматривать сообщения и пользовательские интерфейсы для приложений Chat:

Как открыть конструктор карточек

Требования

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

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

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

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

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

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

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

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

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

Чтобы применить стиль Material Design, не добавляйте атрибут color.

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

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

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

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

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

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

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

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

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

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

Значок "Ещё"Overflow menu можно использовать в карточках Chat, чтобы предлагать дополнительные варианты и действия. Это позволяет добавить больше вариантов, не перегружая интерфейс карточки, и сохранить аккуратный и организованный дизайн.

Как добавить список чипов

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

Как собирать информацию от пользователей

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

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

Сбор текста

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

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

Ниже показана карточка с виджетом приложения TextInput.

Как собирать даты и время

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

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

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

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

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

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

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

Как добавить флажок

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

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

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

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

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

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

Как динамически заполнять раскрывающиеся меню

Доступно для приложений Google Chat.

Вы можете динамически заполнять пункты раскрывающегося меню из источников данных в Google Workspace или из внешнего источника данных. Чтобы использовать динамические источники данных, укажите поле data_source_configs, которое представляет собой массив объектов DataSourceConfig. Каждый тег DataSourceConfig может содержать либо тег platformDataSource, либо тег remoteDataSource. В настоящее время поддерживается только один элемент DataSourceConfig.

Как заполнять элементы из Google Workspace

Чтобы заполнить элементы из источников данных Google Workspace, например пользователей Google Workspace, укажите поле platformDataSource в DataSourceConfig. В отличие от использования статических объектов items, вы опускаете объекты SelectionItem, поскольку эти элементы выбора динамически извлекаются из Google Workspace.

В приведенном ниже коде показано раскрывающееся меню, в котором перечислены пользователи Google Workspace.

JSON

{
  "sections": [
    {
      "header": "Section Header",
      "widgets": [
        {
          "selectionInput": {
            "name": "contacts",
            "type": "DROPDOWN",
            "label": "Select contact from organization",
            "data_source_configs": [
              {
                "platformDataSource": {
                  "commonDataSource": "USER"
                },
                "min_characters_trigger": 1
              }
            ]
          }
        }
      ]
    }
  ]
}
Как заполнять сведения о товарах из внешнего источника данных

Чтобы заполнить элементы из стороннего или внешнего источника данных, например системы управления взаимоотношениями с клиентами (CRM), используйте поле remoteDataSource в объекте DataSourceConfig, чтобы указать функцию, которая возвращает элементы из источника данных.

В приведенном ниже коде показано раскрывающееся меню, которое заполняется элементами из внешнего набора контактов путем выполнения функции getCrmLeads:

JSON

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

Чтобы уменьшить количество запросов к динамическому источнику данных, можно добавить в раскрывающееся меню подсказки, которые будут появляться до того, как пользователь начнет вводить текст. Вы также можете настроить раскрывающийся список так, чтобы элементы заполнялись автоматически на основе того, что вводит пользователь. Для этого задайте значение min_characters_trigger в элементе DataSourceConfig. Когда пользователь вводит количество символов, указанное в min_characters_trigger, запускается функция, заданная в remoteDataSource. Объект события, переданный вашей функции, содержит ввод пользователя в ключе autocomplete_widget_query.

Как добавить меню с возможностью выбора нескольких вариантов

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

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

В приведенном ниже фрагменте кода показано меню с возможностью выбора нескольких вариантов, которое запрашивает и заполняет элементы из внешнего источника данных:

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. Отправьте запрос к источнику данных, используя введенное пользователем значение, чтобы получить одно или несколько значений SelectionItems и предложить их пользователю.
  4. Чтобы вернуть предложенные элементы, верните объект RenderActions с объектом modifyCard.

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

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

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

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

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

Ниже приведена документация по приложениям Chat, которые не являются дополнениями Google Workspace. Чтобы перенести приложение Chat, которое не является дополнением, ознакомьтесь со статьей Как преобразовать приложение Google Chat в дополнение Google Workspace.

В приведенном ниже коде показано меню с возможностью выбора нескольких элементов из внешнего набора контактов для пользователя в приложении Chat, которое не является дополнением. В меню по умолчанию показывается один контакт, а функция 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
  };
}