Программное использование универсального фильтра#
В статье описано, как использовать универсальный фильтр в прикладной разработке: как подключить его в выборке, применять к данным в памяти, передавать условия между формами и сохранять настройки фильтра в jsonb.
Подключение фильтра в выборке#
Для работы универсального фильтра необходимо включить операцию uniFilter и обеспечить работу связанных Avi-методов.
Avi-методы выборки#
onFilterInit— инициализация фильтра. Создает менеджер фильтраfltManager;onApplyFilter— применение фильтра:формирование SQL-макроса;
применение макроса к выборке;
onFilterFinalize— завершение работы фильтра, освобождение ресурсов;lazyInitFilter— ленивая инициализация. Вызывается при первом обращении к фильтру, формирует главную группу;uniFilter— операция фильтра. При активации становится доступен UI фильтрации.
Управление динамическими атрибутами#
Динамические атрибуты используются, когда набор атрибутов, которые возвращает запрос выборки, может изменяться в зависимости от представления, настроек отображения или логики формирования запроса.
Такой сценарий применяется, если универсальный фильтр не может определить доступные атрибуты только по статической разметке выборки или классу. Необходимо явно передать универсальному фильтру список атрибутов, доступных для фильтрации в текущем контексте.
Для управления динамическими атрибутами используется метод fltManager.setDynamicAttrs(...).
Внимание
Динамические атрибуты необходимо устанавливать в onApplyFilter до вызова super.onApplyFilter().
Если вызвать super.onApplyFilter() раньше, макрос универсального фильтра будет сформирован по прежнему набору атрибутов. В результате набор данных выборки уже может измениться, а макрос останется со старыми параметрами фильтрации, из-за чего запрос может завершиться с ошибкой.
Обновление состояния универсального фильтра выполняется при вызове super.onApplyFilter(). Этот вызов применяет актуальный набор динамических атрибутов, формирует SQL-макрос и передает его в фильтр выборки.
Поэтому порядок вызовов критичен: сначала необходимо вызвать fltManager.setDynamicAttrs(...), а затем super.onApplyFilter().
Если вызвать super.onApplyFilter() до установки динамических атрибутов, макрос будет сформирован по предыдущему набору атрибутов. В этом случае набор данных выборки уже может измениться, а условие фильтрации останется прежним, что может привести к ошибке при выполнении запроса.
Пример:
override protected def onApplyFilter(): Unit = {
// Условие, определяющее, какой набор атрибутов будет отображаться в фильтре.
// Например, в зависимости от выбранного значения в другом поле фильтра.
if (Условие) {
// Ситуация: запрос возвращает 2 атрибута.
fltManager.setDynamicAttrs(
Seq(
Btk_FltMetaAttributePkg().buildRefObjectAttr(
"idState",
"Ссылочный тест 1",
"Btk_ClassState"
),
Btk_FltMetaAttributePkg().buildBasicAttr(
"idStateHL",
"Строковый тест 2",
AttributeTypes.Varchar,
isBoolean = false
)
)
)
} else {
// Ситуация: запрос возвращает 1 атрибут.
fltManager.setDynamicAttrs(
Seq(
Btk_FltMetaAttributePkg().buildBasicAttr(
"sCaptionObjectType",
"Строковый атрибут тест 1",
AttributeTypes.Varchar,
isBoolean = false
)
)
)
}
// Обязательный вызов после установки динамических атрибутов.
super.onApplyFilter()
}
Метод setDynamicAttrs передает в фильтр набор атрибутов, которые должны быть доступны пользователю в текущем контексте отображения.
При необходимости можно скрыть атрибуты, которые участвуют в работе фильтра, но не должны отображаться пользователю в списке доступных полей.
Пример скрытия атрибута:
fltManager.setDynamicAttrs(
Seq(
Btk_FltMetaAttributePkg().buildBasicAttr(
"sCaptionObjectType",
"Строковый атрибут тест 1",
AttributeTypes.Varchar,
isBoolean = false
),
Btk_FltMetaAttributePkg().invisibleAttr("idState")
)
)
Также можно указать системное имя группы, для которой задаются динамические атрибуты. Если группа не указана, используется главная группа MainGroup.
fltManager.setDynamicAttrs(
Seq(
Btk_FltMetaAttributePkg().buildBasicAttr(
"sCaptionObjectType",
"Строковый атрибут тест 1",
AttributeTypes.Varchar,
isBoolean = false
),
Btk_FltMetaAttributePkg().invisibleAttr("idState")
),
"Системное имя группы"
)
При изменении набора динамических атрибутов фильтр сопоставляет текущий набор с предыдущим и удаляет устаревшие подобранные элементы из настройки фильтра. Это нужно, чтобы сохраненная настройка не содержала условий по атрибутам, которые больше недоступны в текущем наборе данных выборки.
При сохранении настройки универсального фильтра сохраняется и текущий набор динамических атрибутов. При загрузке настройки система сравнивает сохраненный набор с текущим состоянием динамических атрибутов и удаляет недоступные условия, если они больше не соответствуют текущему набору данных выборки.
Применение макроса на сервере#
В операции onApplyFilter необходимо передать сформированный макрос в серверную часть фильтра выборки:
selection.filters().server.alias = mainGroupAlias
selection.filters().server.condition = fltMacro.where
selection.filters().server.isEnabled = true
alias— алиас основной таблицы выборки;condition— сформированное SQL-условие;isEnabled— признак включения фильтра.
Применение к объектным данным#
Базовый пример#
Применение универсального фильтра к коллекции ROP-объектов в памяти:
// Получаем коллекцию ROP-объектов
val rops = SomeClassApi().getList()
// Применяем универсальный фильтр
val filteredRops = Btk_FltObjectFilterPkg().applyUniFilter(rops, fltManager)
// Работаем с отфильтрованной коллекцией
filteredRops.foreach { rop =>
// обработка объектов
}
Использование в выборке#
Пример применения фильтра в onRefresh, если данные уже загружены:
override def onRefresh: Recs = {
val allRops = loadRopObjects()
val filteredRops = if (fltManager.isActive) {
Btk_FltObjectFilterPkg().applyUniFilter(allRops, fltManager)
} else {
allRops
}
filteredRops
}
Передача фильтра в другую форму#
Для передачи условий используется объект FltCondition.
Пример использования#
val fltCondition = FltCondition()
fltCondition.withMainGroup { mainGroup =>
mainGroup.addAttr("nNumber", 1.nn)
mainGroup.addIsNotNullAttr("sCaption")
mainGroup.addAttr("attr:idPurchaseDirection/attr:sCaption", "643")
mainGroup.cl.addAttr("sCreateUser_dz", "admin")
mainGroup.cl.addDateInterval("dCreateDate_dz", "10.02.2020".nd, "10.02.2021".nd)
mainGroup.addAttr("idRef", Seq(1.nl, 2.nl, 3.nl))
mainGroup.addAttr("idRef", Seq(1.nl, 2.nl, 3.nl), FltAttributeCompareKind.notInList)
mainGroup.addOrGroup { orGr =>
orGr.addAttr("sCaption", "123")
orGr.addAttr("sCaption", "321")
}
mainGroup.withCollection("Btk_SomeCollection") { col =>
col.addAttr("sCaption","someValue")
}
}
fltCondition.withRootGroup("groupName"){group =>
// дополнительные условия
}
// передаем фильтр в форму
Btk_ClassAvi.defList.newForm()
.params(Map("uniFilterCondition_dz" -> fltCondition))
.open()
Внимание
Чтобы добавить условия по атрибуту класса, отсутствующему в дата-сете, и объединить их в группу, используйте .cl перед добавлением группы, а не атрибута.
Корректный пример#
fltCondition.withMainGroup { mainGroup =>
mainGroup.cl.addOrGroup { orGr =>
orGr.addAttr("attr:id/attr:jAttrDataDeb_sCaption", "123")
orGr.addAttr("attr:id/attr:jAttrDataCr_sCaption", "123")
}
}
Некорректный пример#
fltCondition.withMainGroup { mainGroup =>
mainGroup.addOrGroup { orGr =>
orGr.cl.addAttr("attr:id/attr:jAttrDataDeb_sCaption", "123")
orGr.cl.addAttr("attr:id/attr:jAttrDataCr_sCaption", "123")
}
}
Примечание
Путь до атрибута можно узнать через отладчик (Ctrl+Alt+Shift+W).
Для ссылочных атрибутов указывайте полный путь, например attr:idPurchaseDirection/attr:sCaption.
Для обычных атрибутов используйте сокращенный путь, например nNumber.
Настройка отдельного поля фильтра#
Позволяет хранить настройки фильтра в поле типа jsonb и отображать их как единый элемент.

Структура дополнительных полей#
case class Row(
var sCompareKindName: NString,
var sCompareKindCaption: NString,
var sFilterValue: NString,
var sFilterValueEditor: NString,
var bFilterValueRo: Boolean
)
Реализация в onRefresh#
override def onRefresh: Recs = {
val arrBuffer = ArrayBuffer()
for (rop <- new OQuery(Btktst_FltStandaloneAta.Type) {}) {
val standaloneAttribute = FltStandaloneAttribute.refAttr(
sRefClass = "Bs_Goods",
sData = rop.get(_.jData)
)
val displaySettings = Btk_FltStandaloneLib().getDisplaySettings(standaloneAttribute)
arrBuffer += ((
rop,
Row(
displaySettings.sCompareKindName,
displaySettings.sCompareKindCaption,
displaySettings.sFilterValue,
displaySettings.sFilterValueEditor,
displaySettings.bFilterValueRo
)
))
}
arrBuffer.toVector
}
Реализация в onRefreshItem#
override def onRefreshItem: Recs = {
val rop = thisRop()
val standaloneAttribute = FltStandaloneAttribute.refAttr(
sRefClass = "Bs_Goods",
sData = rop.get(_.jData)
)
val displaySettings = Btk_FltStandaloneLib().getDisplaySettings(standaloneAttribute)
(
rop,
Row(
displaySettings.sCompareKindName,
displaySettings.sCompareKindCaption,
displaySettings.sFilterValue,
displaySettings.sFilterValueEditor,
displaySettings.bFilterValueRo
)
)
}
Блокировка редактирования значения#
override def checkWorkability(): Unit = {
super.checkWorkability()
selection.attrs("sFilterValue").isReadOnly =
selection.getSelfVar("bFilterValueRo").asBoolean()
}
Сеттеры#
@Setter(refreshAfter = true, cwaAfter = true)
def setsCompareKindName(event: SetterEvent): Unit = {
val res = Btk_FltStandaloneLib().setCompareKindName(
FltStandaloneAttribute.refAttr("Bs_Goods", thisRop().get(_.jData)),
event
)
if (res.hasChange) {
thisApi().setjData(thisRop(), res.attributeValue.buildJson().toNString)
}
}
@Setter(refreshAfter = true, cwaAfter = true)
def setsFilterValue(event: SetterEvent): Unit = {
val res = Btk_FltStandaloneLib().setFilterValue(
FltStandaloneAttribute.refAttr("Bs_Goods", thisRop().get(_.jData)),
event
)
if (res.hasChange) {
thisApi().setjData(thisRop(), res.attributeValue.buildJson().toNString)
}
}
Настройка avm.xml#
<attributes>
<attr name="sCompareKindName" isVisible="false"/>
<attr name="sCompareKindCaption" isVisible="true" order="100" caption="Вид сравнения">
<editor>
<lookup changeableAttr="sCompareKindName"
isLookupLazyLoad="true"
lookupKeyAttr="sName"
lookupListAttr="sCaption"
lookupQuery="gtk-Btk_FltAttributeCompareKindAvi#Lookup_ByParams"
isResetButtonVisible="true">
<params>
<param name="names"
value="Equal;InEqual;InValueSet;InList;InterValMnemoCode"
dataType="String"/>
</params>
</lookup>
</editor>
</attr>
<attr name="sFilterValue" isVisible="true" order="110" caption="Значение">
<editor editorTypeAttr="sFilterValueEditor"/>
</attr>
<attr name="sFilterValueEditor" isVisible="false"/>
<attr name="bFilterValueRo" isVisible="false"/>
</attributes>
Примечание
Доступные типы сравнения указываются в <params> через ;.
Перечень системных имен см. в FltAttributeCompareKind.