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

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

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

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

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


Используйте Card Builder для разработки и предварительного просмотра сообщений и пользовательских интерфейсов для приложений 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, которые выполняются, когда пользователь выбирает или отменяет выбор элемента в поле ввода.

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

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

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

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

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

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

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

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

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

Динамическое заполнение раскрывающихся меню

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

Вы можете динамически заполнять элементы раскрывающегося меню из источников данных в Google Workspace или из внешнего источника данных. Чтобы использовать динамические источники данных, укажите поле dataSourceConfigs, которое представляет собой массив объектов 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",
            "dataSourceConfigs": [
              {
                "platformDataSource": {
                  "commonDataSource": "USER"
                },
                "minCharactersTrigger": 1
              }
            ]
          }
        }
      ]
    }
  ]
}
Заполнить элементы из внешнего источника данных

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

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

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

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

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

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

  • Пользователи Google Workspace. Вы можете добавлять только пользователей из той же организации Google Workspace.
  • Чат-группы. Пользователь, который вводит элементы в меню множественного выбора, может просматривать и выбирать только чат-группы, к которым он принадлежит в организации Google Workspace.

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

В коде ниже показано меню множественного выбора пользователей Google Workspace. Чтобы заполнить список пользователей, в поле ввода задается значение USER для commonDataSource:

JSON

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

В коде ниже показано меню чат-групп Chat с множественным выбором. Чтобы заполнить чат-группы, в виджете выбора указывается поле hostAppDataSource. В меню множественного выбора также задается значение true для defaultToCurrentSpace, чтобы текущая чат-группа была выбрана в меню по умолчанию:

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.

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

Если для действия задано значение "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
              }
            ]
          }
        }
      ]
    }
  ]
}
Задайте действие allWidgetsAreRequired в 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":{
              "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 не появляется сообщение об ошибке, но приложение 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
  };
}