Как установить отбор «В списке» для табличной части на управляемой форме в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Переход с обычных форм на управляемые формы в платформе 1С:Предприятие 8 принес разработчикам множество преимуществ, но вместе с тем и ряд ограничений. Одной из классических проблем, с которой сталкиваются программисты при адаптации старого кода или разработке новых интерфейсов, является программная установка сложных отборов в табличных частях и таблицах значений.

В обычных формах отбор в табличном поле настраивался чрезвычайно просто. Разработчику был доступен полноценный объект отбора, где можно было указать конкретное значение, использование, а главное — вид сравнения, включая ВидСравнения.ВСписке или ВидСравнения.НеВСписке. В управляемых формах поведение этого механизма принципиально изменилось. Давайте подробно разберем, почему возникает эта проблема и какие существуют надежные способы ее решения.

Выясняем причину: почему платформа накладывает ограничения?

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

Для быстрой фильтрации строк на клиенте у элемента формы «Таблица» предусмотрено свойство ОтборСтрок. Однако это свойство принимает исключительно тип ФиксированнаяСтруктура. Механизм работы фиксированной структуры на уровне платформы заложен жестко: отбор выполняется только по принципу строгого равенства («Равно»). Платформа физически не умеет разбирать внутри структуры сложные коллекции (массивы, списки значений) для выполнения условий типа «В списке».

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


// Внимание! Этот код НЕ будет работать так, как ожидает разработчик:
МассивНоменклатуры = Новый Массив;
МассивНоменклатуры.Добавить(Ссылка1);
МассивНоменклатуры.Добавить(Ссылка2);

// Платформа попытается найти строки, где Номенклатура буквально равна объекту Массив
Элементы.Материалы.ОтборСтрок = Новый ФиксированнаяСтруктура("Номенклатура", МассивНоменклатуры);

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

Способ 1. Фильтрация с помощью служебной булевой колонки

Этот метод считается наиболее популярным и «жизнеспособным» в тех случаях, когда пользователю необходимо продолжать активно редактировать строки отфильтрованной табличной части (добавлять, удалять или изменять данные непосредственно в форме).

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

Разберем реализацию этого способа по шагам:

  1. Создание реквизита: Откроем редактор формы. В окне «Реквизиты» найдем нашу табличную часть (например, Товары) и добавим в нее новый реквизит формы. Назовем его ПодходитПодОтбор, тип данных — Булево. Обратите внимание: добавлять этот реквизит в метаданные самого документа или обработки в конфигураторе не нужно, он должен существовать только в контексте формы, чтобы не засорять базу данных.
  2. Вывод на форму: Перетащим созданный реквизит в элементы формы, но снимем у него флаг Видимость, чтобы пользователь не видел техническую колонку.
  3. Программное заполнение флага: Напишем процедуру на сервере, которая будет обходить строки таблицы, проверять вхождение анализируемого реквизита в наш список значений и проставлять соответствующий флаг.
  4. Наложение отбора: Установим отбор по нашему булевому реквизиту со значением Истина.

Посмотрим на практический пример реализации этого алгоритма:


&НаСервере
Процедура УстановитьОтборВСпискеНаСервере(СписокРазрешенныхТоваров)
    
    // 1. Обходим строки табличной части и заполняем булеву колонку
    Для Каждого СтрокаТЧ Из Объект.Товары Цикл
        // Проверяем, входит ли товар в переданный список значений
        Если СписокРазрешенныхТоваров.НайтиПоЗначению(СтрокаТЧ.Номенклатура) <> Неопределено Тогда
            СтрокаТЧ.ПодходитПодОтбор = Истина;
        Иначе
            СтрокаТЧ.ПодходитПодОтбор = Ложь;
        КонецЕсли;
    КонецЦикла;
    
    // 2. Накладываем отбор по нашей служебной колонке
    СтруктураОтбора = Новый ФиксированнаяСтруктура("ПодходитПодОтбор", Истина);
    Элементы.Товары.ОтборСтрок = СтруктураОтбора;
    
КонецПроцедуры

Для сброса такого отбора достаточно просто очистить свойство таблицы на клиенте или сервере:


&НаКлиенте
Процедура СброситьОтбор()
    Элементы.Товары.ОтборСтрок = Неопределено;
КонецПроцедуры

Важное замечание по производительности: Данный метод отлично работает на табличных частях малого и среднего объема (до нескольких тысяч строк). Если в вашей табличной части содержится более 10 000 строк, полный обход коллекции в цикле на сервере и последующая отрисовка интерфейса могут вызвать заметные задержки («зависание» формы). В таких случаях рекомендуем рассмотреть другие архитектурные варианты.

Способ 2. Метод двух таблиц (буферной и отображаемой) с фильтрацией запросом

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

Рассмотрим, как реализовать этот подход программно. Мы можем выгрузить данные буферной таблицы, пропустить их через запрос с условием ГДЕ Номенклатура В (&СписокНоменклатуры) (или задействовать ПостроительЗапроса), а полученный результат загрузить обратно в видимую на форме таблицу.

Посмотрим на пример использования ПостроительЗапроса для быстрой фильтрации выгруженной таблицы значений:


&НаСервере
Процедура ВыполнитьОтборПостроителем(СписокНоменклатурыДляОтбора)
    
    // Выгружаем исходные данные из объекта в полноценную таблицу значений
    ТаблицаДанных = Объект.Товары.Выгрузить();
    
    // Инициализируем построитель запроса
    Построитель = Новый ПостроительЗапроса;
    Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТаблицаДанных);
    
    // Добавляем условие отбора по нужной колонке
    СтрокаОтбора = Построитель.Отбор.Добавить("Номенклатура");
    СтрокаОтбора.ВидСравнения = ВидСравнения.ВСписке;
    СтрокаОтбора.Значение = СписокНоменклатурыДляОтбора;
    СтрокаОтбора.Использование = Истина;
    
    // Выполняем фильтрацию силами платформы
    Построитель.Выполнить();
    ТаблицаРезультат = Построитель.Результат.Выгрузить();
    
    // Очищаем отображаемую на форме таблицу и загружаем отфильтрованные строки
    // Предполагается, что мы выводим на форму реквизит "ОтображаемыеТовары"
    Объект.ОтображаемыеТовары.Загрузить(ТаблицаРезультат);
    
КонецПроцедуры

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

Способ 3. Переход на Динамический список

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

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

Разберем по шагам, как настроить отбор «В списке» для динамического списка:


&НаСервере
Процедура УстановитьОтборВДинамическомСписке(МассивЗначений)
    
    // Очищаем старые отборы списка
    Список.Отбор.Элементы.Очистить();
    
    // Создаем новый элемент отбора компоновки данных
    ЭлементОтбора = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    
    // Указываем левое значение (поле, по которому фильтруем)
    ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Номенклатура");
    
    // Устанавливаем вид сравнения "В списке"
    ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
    
    // Передаем нашу коллекцию в правое значение
    ЭлементОтбора.ПравоеЗначение = МассивЗначений;
    ЭлементОтбора.Использование = Истина;
    
    // При необходимости делаем отбор недоступным для изменения пользователем
    ЭлементОтбора.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный;
    
КонецПроцедуры

Этот подход является наиболее производительным, так как все отборы накладываются на уровне СУБД, минимизируя объем данных, передаваемых между сервером базы данных, сервером приложений 1С и тонким клиентом.

Критически важное предостережение: почему нельзя скрывать строки через условное оформление?

Среди разработчиков часто встречается идея использовать механизм условного оформления формы для реализации сложных отборов. Идея проста: добавить условие отбора в условное оформление и установить свойство Видимость = Ложь для строк, не соответствующих условиям.

Мы настоятельно не рекомендуем использовать этот способ по следующим причинам:

Подведем итоги

Мы выяснили, что напрямую передать список значений в свойство ОтборСтрок управляемой формы невозможно из-за ограничений типа ФиксированнаяСтруктура. Чтобы решить эту задачу, выберите один из проверенных подходов:

← На главную