Смарт-формы
Мотивация
Довольно часто нужно нарисовать типовую форму для добавления или редактирования строки таблицы Логирование через ObjectLogService будет выполнено автоматически, а значит и Шина сможет принять событие, если будет нужно.
Автоматическая генерация формы
Форма генерируется по внутреннним правилам, но подвигать контролы можно
через их позициии в списке fields
в yaml файле или атрибут order
, но даже это не гарантирует,
что они выстроятся в нужном вам порядке - мета сортирует блоками в соответствии со внутренними правилами.
Тип me-input так же пытается подобраться автоматически, исходя из заполненности полей:
- name
- db_type
- data_type
- semantic_role
Ширина me-input для поля высчитывается автоматически исходя из разных параметров,
но может быть задана через атрибут поля lego_elem.span
.
В целом иногда есть задача вывести поле как textarea, для этого используется атрибут lego_elem
.
По сути там пишется все то, что можно писать в обычных ручных lego формах, но не обязательно описывать вообще все -
впишите только то, что нужно переопределить по отношению к автосгенерированным свойствам.
Например:
...
- alias: name
db_type: text
is_title: true
display_name: Название клиента
name: name
semantic_role: DIMENSION
order: 100 <---------- сортировка, заданная вручную и не через само положение относительно других полей entity
data_type: TEXT
is_required: true
lego_elem: <---- Все то, что работает в lego формах (НЕ ОЯЗАТЕЛЬНО полное описание всего elem). Перетирается важными сгенерированными параметрами, например id, label, entityId, required и пр
name: me-input
span: 4
attrs:
type: textarea
...
Настройка insert_query
Иногда бывает, что надо вызвать хранимую процедуру для вставки объекта.
Это можно сделать через настройку параметра insert_query в yaml, нужной entity.
Данные формы придут как обычно в :env.sp.obj.*
ВАЖНО! Вы будете обязаны вернуть одну строку с одним полем с названием id
.
default_value
Значение default_value шаблонизируется через freemarker так же как и любой шаблон и скрипт в мете. Из этого следует, что вы можете рассчитывать динамические значения, например дату по умолчанию. Вот пример одного поля:
- name: "pay_date"
db_type: "date"
default_value: "${ref.now.plusMonths(1)?date}"
display_name: "Дата оплаты"
i18n: true
Пример и документация использования Java API для работы с датами: https://samples-demo.devision.io/page?p=3750&a=35 (opens in a new tab)
Параметры add_expression и set_expression
Иногда при insert или update записи надо иметь высчитываемые поля, но часто они зависят от контекста, например userId
.
В примере нише рассматривается простейший случай, когда нам надо просто взять и вставить id пользователя (используется prepared statement), но
тут можно написать и более сложный sql expression
- name: author_user_id
db_type: bigint
display_name: Добавлено
i18n: true
foreign_entity_id: 2654
is_readonly: true
add_expression: :env.userId
- name: last_user_id
db_type: bigint
display_name: Изменено
i18n: true
foreign_entity_id: 2654
is_readonly: true
add_expression: :env.userId
set_expression: :env.userId
Связанный enum
Enum-ы хранятся в таблице meta.enum и являются простыми справочниками, для которых не хочется заводить отдельные таблицы.
В смарт-формах можно очень легко автоматически получить контрол me-select для таких полей просто сделав ссылку на нужный вам справочник.
...
- db_type: text
name: state
foreign_enum_kind: 'billing_state' <-------- эта настройка управляет ссылкой на enum
lego_elem:
span: 4
...
Мета внутри себя для такого поля сделает запрос:
SELECT * FROM meta.enum_options_view WHERE kind=:kind ORDER BY name
В плейсхолдер :kind
будет подставлено значение из foreign_enum_kind
, в этом конкретном примере - billing_state
.
Практический пример
Пример настройки entity
id: '190'
name: Клиент
db_alias: adplatform
schema: public
table: client
alias: client
kind: DICT
metaql_where: '#table.id in (select client_id from get_user_clients(${env.userId?number}))'
input_options_query: >
select
client.id,
client.name,
categories.name as description
from client
left join company on company_id = company.id
left join client_category as categories on categories.id = client.category_id
where client.is_enabled
AND client.company_id=:env.companyId::bigint
AND client.id in (select client_id from get_user_clients(:env.userId::bigint) )
ORDER BY 2
insert_query: >
SELECT client_id as id FROM ext_garpun_main."api_addClient"(
:env.userId::bigint, :env.companyId::bigint,
:env.sp.obj.name::text, 11
);
search_order: 1
object_list_base_page_id: '2176'
acl:
view: /* Не забывайте про ACL. У вас он может быть другой */
roles:
- meta.role.auth
fields:
- alias: id
db_type: bigint
is_primary: true
name: id
display_name: ID
semantic_role: DIMENSION
data_type: LONG
is_disabled: true
- alias: name
db_type: text
is_title: true
display_name: Название клиента
name: name
semantic_role: DIMENSION
data_type: TEXT
is_required: true
- alias: is_enabled
db_type: bool
name: is_enabled
display_name: Активный
semantic_role: DIMENSION
data_type: BOOLEAN
- alias: is_archived
db_type: bool
name: archive
display_name: Архивный
semantic_role: DIMENSION
data_type: BOOLEAN
- alias: category_id
db_type: int8
name: category_id
data_type: LONG
foreign_entity_id: 2768
semantic_role: DIMENSION
default_value: 11
- alias: creation_time
db_type: timestamp(0)
name: creation_time
display_name: Дата создания
i18n: true
data_type: DATETIME
semantic_role: DIMENSION
is_disabled: true
- alias: modification_time
db_type: timestamp(0)
name: modification_time
display_name: Дата изменения
i18n: true
data_type: DATETIME
semantic_role: DIMENSION
is_disabled: true
Пример 1
Например, мы хотим получить форму редактирования всех полей нужной entity_id.
Добавляем страницу в рамках нужной entity.
В нашем случае для примера 190. Url меты должен быть примерно таким, когда вы работаете со страницей - /card?e=190&o={ТУТ_ID_ОБЪЕКТА}
Это все, что надо на странице:
<script type="meta/js" elem="me-smart-form" id="smart_form">
function main(SmartFormService, pvm, vm, env, originalEnv) {
SmartFormService.handle(env, originalEnv, vm, pvm);
}
</script>
Мета автоматически нарисует текстовые поля для ввода текстовых значений, чекбоксы для boolean, нарисует выпадашки для полей, с указанным foreign_entity_id
Пример 2
Мы хотели получить редактор записи клиента, но так, чтобы при добавлении было только поле name, а при редактировании к нему добавлялся бы выбор category_id
Это все, что надо на странице:
<script type="meta/js" elem="me-smart-form" id="smart_form">
function main(SmartFormService, pvm, vm, env, originalEnv) {
var fieldNames = ['name'];
if (env.hasObjectId) {
fieldNames.push('category_id');
}
SmartFormService.handle(env, originalEnv, vm, pvm, {
fieldNames: fieldNames
});
}
</script>
Что далее?
Не забудьте про обработчик событий сущности check_access для того, чтобы не делать проверку доступа к объекту на каждой странице сущности.
Новый прием в смартформах ☝🏽👾👾👾
раньше в ямлах в запросе input_options_query мы могли использовать ТОЛЬКО параметры, которые ВСЕГДА передаются в этот запрос или которые доступны из окружения (например :env.companyId).
📍теперь можно использовать параметры, которые не всегда передаются в этот запрос при вызове сущности Сотрудник (например: COALESCE(NULLIF('${env.sp.date}', '')::date, '1970-01-01'::date) ) т.е. параметр не всегда передается в запрос. в моем примере он передается только из формы (:env.entityId = 'employee_distribution_time'), во всех других обращениях к этой сущности ничего дополнительно не передается.
- ранее не работала передача параметра через шаблонизацию, при выкатке последнего мастера эту возможность добавили.
- параметр необходимо указывать в кавычках, чтобы он был текстом, тогда, если параметр не придет в запрос, то в запросе окажется просто пустая строка и запрос не упадет.
- если требуется приведение типов, то пустую строку нужно обработать через COALESCE