Table form

Создание компонента

Компонент Table Form позволяет отображать выбранные значения в виде таблицы.

Созданные записи являются временными и хранятся в памяти браузера. Отправляются на сервер вместе с родительской формой.

Для отображения кнопки Создать и возможности вызова формы создания используется formKey*, который может быть вычислен в зависимости от настройки Типа данных на вкладке Данные:

  • Журнал - варианты создания вычисляются с помощью настройки ID журнала, либо на основе типа ассоциации, которая указана в атрибуте (при этом ID журнала заполнять не нужно). Есть возможность выбрать колонки для отображения в таблице.



../../../../../_images/Table_form_3.png
  • Пользовательские - варианты создания вычисляются на основе типа ассоциации, которая указана в атрибуте. При этом требуется добавить вручную колонки для отображения в таблице (соответствуют названиям полей в форме создания).



../../../../../_images/Table_form_6.png

Варианты создания определяются из указанного аттрибута на вкладке API, либо из настройки Пользовательские варианты создания на вкладке Данные. Настройки колонок берутся из первого по списку полученного варианта создания. Если нет ни одного доступного варианта создания, кнопка Создать будет скрыта.

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

../../../../../_images/Table_form_7.png

Таким образом можно установить заголовок колонки (может быть задан в двух локализация), тип данных и признак Выбор нескольких.

Предусмотрена возможность применения к колонке форматтера вручную. Опция Установить атрибуты вручную не влияет на настройку форматтера. Поле настройки форматтера принимает значение вида:

 value = {
  type: 'script',
  config: {
    fn: function(...params) {
      const linkHtml = `<a href="${params[0].cell}">${params[0].cell}</a>`
      return {
        type: 'html',
        config: {html: linkHtml}
      }
    }
  }
};
../../../../../_images/Table_form_8.png

Привязка к атрибуту родительского типа

Для корректной связи tableForm с родительской записью необходимо указать атрибут-ассоциацию на вкладке API в поле Свойство (Property Name), либо в блоке Свойства (Properties) задать ключ attribute со значением — именем атрибута на родительском типе.

Например, если на родительском типе определён атрибут:

- id: risks
  type: ASSOC
  config:
    typeRef: emodel/type@project-risk-item
    child: true
  multiple: true

То в настройках tableForm:

  • key компонента = risks (совпадает с id атрибута), или

  • в блоке Properties указать attribute = risks

Без этой привязки tableForm не сможет сохранить дочерние записи при отправке родительской формы.

Кнопки «Создать» и «Выбрать»

Компонент Table Form поддерживает два способа добавления записей:

  • Создать — открывает пустую форму создания новой записи. Запись создаётся с нуля как дочерний элемент родительской сущности. Варианты создания определяются из журнала, указанного в source.journal.journalId, или из типа ассоциации атрибута.

  • Выбрать — открывает журнал-справочник, из которого можно выбрать существующую запись. Выбранная запись используется как шаблон: она копируется (клонируется) в таблицу как новая дочерняя запись. Оригинал в справочнике не изменяется.

Кнопка «Выбрать» отображается только при установленном флаге Кнопка «Выбрать» и указанном selectJournalId (идентификатор журнала для выбора шаблонов).

Примечание

Журнал для кнопки «Создать» (source.journal.journalId) и журнал для кнопки «Выбрать» (selectJournalId) — это, как правило, разные журналы. Первый отображает дочерние элементы в таблице, второй — справочник шаблонов для копирования.

Выбор значения из справочника

Чтобы на форме отображалась кнопка «Выбрать» для копирования существующих объектов, установите флаг Кнопка «Выбрать»:

../../../../../_images/select_01.png

Укажите идентификатор журнала для выбора объектов. Если идентификатор не указан, будет использован журнал соответствующий типу первого варианта создания.

  1. На форме нажмите кнопку «Выбрать».

  2. Выберите необходимый объект для копирования. Выбираемое значение используется как шаблон для копируемого объекта.

../../../../../_images/select_02.png
  1. Открывается форма редактирования объекта, если необходимо, то можно изменить значения.

../../../../../_images/select_03.png
  1. Подтвердите выбор, объект будет скопирован в table form.

Структура типов для корректной работы копирования

Чтобы копирование из справочника работало правильно (записи создавались как дочерние элементы родительской сущности и не попадали в глобальный справочник), необходима двухуровневая иерархия типов:

data-list
  └── <тип-справочник>            ← справочник (шаблоны для выбора)
        └── <тип-элемент>         ← дочерний тип (создаётся при копировании)

Пример из CRM (продукты и услуги):

# Тип-справочник (глобальный каталог шаблонов)
id: product-and-services
parentRef: emodel/type@data-list

# Тип-элемент (дочерняя запись сделки/заказа)
id: product-and-services-item
parentRef: emodel/type@product-and-services

Конфигурация ассоциации на родительском типе (сделка/заказ):

- id: product-and-services
  type: ASSOC
  config:
    typeRef: emodel/type@product-and-services-item  # ← тип-элемент, не справочник
    child: true
  multiple: true

Конфигурация компонента Table Form на форме:

{
  "key": "has-pas:product-and-services",
  "type": "tableForm",
  "source": {
    "type": "journal",
    "journal": {
      "journalId": "product-and-services-item"
    }
  },
  "enableSelectButton": true,
  "selectJournalId": "product-and-services-template",
  "properties": {
    "attribute": "has-pas:product-and-services"
  }
}

Где:

  • source.journal.journalId — журнал типа-элемента (для отображения строк в таблице)

  • selectJournalId — журнал типа-справочника (для выбора шаблонов при нажатии кнопки «Выбрать»)

  • properties.attribute — атрибут-ассоциация на родительском типе

Важно

Если тип-элемент наследуется напрямую от data-list, созданные записи будут попадать в глобальный справочник. Тип-элемент должен наследоваться от типа-справочника (или другого не-справочного типа), чтобы записи существовали только как дочерние элементы родительской сущности.

Наполнение данных в таблице

Использование Массив записей

Для этого можно использовать компонент Async Data (добавить ссылку) с типом данных Массив записей (Records Array).

В поле Массив записей (Records Array) указывается имя таблицы, заключенное в двойные фигурные скобки, в аттрибутах - поля этой таблицы и ключи, по которым они будут доступны.

../../../../../_images/Table_form_9.png

Ручное заполнение

Table form ожидает, что value в нем будет массивом recordRef. Данные по рекордам будут подтянуты автоматически.

Таким образом, можем в async data получить какие-то данные, например, через recordsQuery, а в table form (вкладка Данные -> Вычисляемые значения) преобразовать их в массив recordRef и назначить в value:

const uncompletedIssues = _.get(data, 'uncompletedIssues.records') || [];
const issueIds = uncompletedIssues.map(issue => issue.id);

value = issueIds;

Как получить доступ к родительской форме?

Чтобы получить доступ к родительской форме из формы создания/редактирования записи, можно использовать объект instance.options.parentForm. Например, если требуется установить значение по умолчанию полю дочерней формы, опираясь на значение из поля родительской формы, можно установить этому полю настройку Пользовательские значения по умолчанию:

  if (instance.options.parentForm) {
value = instance.options.parentForm.getValue()['data']['someParentFormFieldName']
  }
../../../../../_images/Table_form_10.png

Отображение действий

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

Есть два варианта установки действий:

  1. Использовать действия журнала — флаг на вкладке Отображение. При включении в строках таблицы отображаются действия, заданные в конфигурации журнала (секция actions в YAML журнала). Например, если в журнале указаны uiserv/action@edit и uiserv/action@delete, в каждой строке таблицы будут доступны кнопки редактирования и удаления. Рекомендуется включать, когда источник данных — Журнал.

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

    • Если установлен флаг Использовать действия журнала, то из поля Действия компонентов будет обработано только действие Создать.

Наименование действия

Ключ

По умолчанию

disabled / режим просмотра

Создать

create

true

недоступно или скрыто

Редактировать

edit

true

недоступно

Удалить

delete

true

недоступно

Просмотр

view

true

Предпросмотр документа

preview

false

Клонировать

clone

false

Поле принимает javascript-выражение, в котором необходимо присвоить переменной value объект с необязательными свойствами create, view, preview, edit и delete (ключи в таблице выше).

Если какое-то из свойств не указано, то компонент сам будет решать, отображать элемент или скрывать, в зависимости от ситуации (*).

Если компонент Table Form в состоянии disabled или форма в режиме просмотра, элементы “редактировать“, “удалить“ в любом случае будут недоступны.

value = {
  create: false,
  view: false,
  edit: false,
  delete: true,
  preview: true
}
../../../../../_images/Table_form_11.png

Настройки элементов

Вкладка Отображение настройка Элементы отображения задает некоторые свойства действиям.

  • Копирование без подтверждения

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

../../../../../_images/Table_form_12.png

Выбор строк в таблице и выполнение действий с выбранными строками

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

../../../../../_images/Table_form_13.png

Для получения списка выбранных строк в таблице у компонента предусмотрен метод getSelectedRows().

Например, чтобы выполнить какое-нибудь действие с выбранными строками в таблице при нажатии на кнопку, в компоненте Button выберем Действия: Custom и укажем Button Custom Logic:

const tableFormComponent = utils.getComponent(form.components, 'tableFormComponentKey');
if (!tableFormComponent) {
    return;
}

const selectedRows = tableFormComponent.getSelectedRows();

// some actions with selectedRows

Запрет выбора некоторых строк в таблице

На вкладке Данные в поле Указать невыбираемые строки можно запретить выбор определенных строк в таблице:

../../../../../_images/Table_form_14.png

Установка выбранных строк по умолчанию

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

../../../../../_images/Table_form_15.png

Флаг «Разрешить ручное переопределение вычисляемого значения»/ «Allow Manual Override of Calculated Value»

../../../../../_images/Table_form_18.png

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

См. подробно

Статические заголовки

Для включения статического заголовка необходимо в родительской форме проставить чекбокс Скрыть имя записи в модальном заголовке/ Hide record name in modal title. В данном случае произойдет скрытие имени ноды.

Так же, есть возможность задать свой локализованный статический заголовок. Для этого необходимо:

  1. Создать в Локализация/Localization бандл с необходимой локализацией

  2. Вставить ключ локализации в поле Укажите пользовательское статическое название поля для объединения с основным/ Enter custom static label for concat with basic. Если данный ключ не будет найден, то в заголовок добавится тот текст, что лежит в поле Укажите пользовательское статическое название поля для объединения с основным/ Enter custom static label for concat with basic

Импорт из файла

Появилась возможность импортировать в таблицу данные из файла (например, xslx). Для этого в настройки компонента добавлен раздел Импорт:

../../../../../_images/Table_form_16.png

Поле URL для загрузки предназначено для указания url, куда будет отправляться POST-запрос с прикреплённым файлом (или несколькими файлами) для обработки. Обязательно к заполнению.

Поле Обработчик ответов предназначено для обработки результата ответа сервера после загрузки файла. Обязательно к заполнению. Принимает javascript-выражение, в котором необходимо присвоить переменной result строку (recordRef), массив строк (массив recordRef) или ошибку (result = new Error(„текст ошибки“)).

В javascript-выражении помимо стандартных объектов formio (data, instance, _, moment, и т.д.) доступна переменная response (или resp), которая содержит результат ответа сервера после загрузки файла.

После заполнения настроек у компонента должна появиться кнопка Импорт:

../../../../../_images/Table_form_17.png

Примеры

Импортирование данных

Работа с файлами обычно делегируется бэкенду.

Создадим новый тип для импортирования данных excel-files-import.yml и форму excel-files-inport-form.json

Для отображения строк используем дочерний тип excel-files-import-line.yml

ecos-app архив со всеми артефактами, который можно загрузить целиком по адресу v2/journals?journalId=ecos-apps&viewMode=table&ws=admin$workspace

Далее необходимо добавить основной тип в меню, открыть журнал, нажать «Создать», добавить файлы для импорта и нажать «Создать». На бэкенде ловится событие создания, обрабатываются файлы и заполняются строки.

Примерный вид, того как это будет выглядеть:

../../../../../_images/example_01.jpg

../../../../../_images/example_02.jpg

Для произвольной логики на бэкенде в Citeck есть возможность создания дополнительных микросервисов. Демо микросервис, который можно посмотреть для примера - https://github.com/Citeck/ecos-demo-app

Изменение стиля строки

../../../../../_images/example_03.jpg

У каждой строки можно заполнять флаг «совпадения» и повесить на этот атрибут форматтер

Пример формы excel-files-inport-form.json

Добавление строк

Таблицы на формах - способ представления дочерних сущностей.

Добавлять новые строки на бэкенде можно, указав корректные атрибуты _parent (ссылка на родителя) и _parentAtt (ассоциация родителя для связи с дочерней сущностью).

recordsService.create("type@excel-files-import-line", ObjectData.create()
    .set("name", "custom-name")
    .set("validLine", true)
    .set(RecordConstants.ATT_PARENT, "emodel/excel-files-import@0fd3353c-8a9f-47bc-b3f9-26ec92b1245c")
    .set(RecordConstants.ATT_PARENT_ATT, "importedLines")
);