Как сделать отбор в табличном поле с источником "Таблица значений" на обычных формах в 1С?

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

При разработке внешних обработок или недокументированных функций в конфигурациях на обычных формах (1С 8.1, 8.2 или 8.3 в режиме совместимости) программисты часто сталкиваются со следующей проблемой. На форму добавляется табличное поле, его типом значений назначается ТаблицаЗначений, данные программно заполняются, но при попытке установить стандартный отбор с помощью конструкции вида ЭлементыФормы.ИмяТаблицы.Значение.Отбор система выдает критическую ошибку: "Поле объекта не обнаружено (Отбор)".

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

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

Способ 1. Использование полноценной Табличной Части (Рекомендуемый)

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

Рассмотрим по шагам, как настроить этот механизм:

  1. Добавьте в метаданные вашей обработки (или в реквизиты формы, если это форма объекта) табличную часть, например, с именем ТабХарактеристик.
  2. Перенесите туда все необходимые колонки (реквизиты табличной части).
  3. Разместите табличную часть на форме в виде табличного поля.
  4. Для программной установки фильтра используйте свойство ОтборСтрок.

Посмотрим на пример кода, который устанавливает отбор в табличной части по конкретной номенклатуре:


// Установка отбора по выбранной номенклатуре
ЭлементыФормы.ТабХарактеристик.ОтборСтрок.Номенклатура.Установить(ВыбраннаяНоменклатура);

// Чтобы отключить отбор и снова показать все строки, используйте:
ЭлементыФормы.ТабХарактеристик.ОтборСтрок.Номенклатура.Установить(, Ложь);

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

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

Если вам обязательно нужно работать именно с объектом ТаблицаЗначений и при этом сохранять возможность интерактивного редактирования строк пользователем, мы можем применить архитектурный паттерн "Модель-Представление".

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

Проанализируем этот алгоритм более детально по шагам.

Шаг 1. Инициализация и подготовка таблиц

В модуле формы объявим переменную для хранения полной таблицы значений:

Перем тзИсходная; // Здесь будут храниться абсолютно все данные

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


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

Шаг 2. Заполнение данными и генерация ключей

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


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

Шаг 3. Наложение отбора (Обновление отображаемой таблицы)

Теперь создадим процедуру, которая будет отбирать строки из тзИсходная по заданному критерию и переносить их в отображаемую таблицу тзНаФорме:


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

Шаг 4. Синхронизация изменений при редактировании

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


Процедура ТабХарактеристикПриОкончанииРедактирования(Элемент, НоваяСтрока, Копирование)
    
    ТекущаяСтрокаФормы = Элемент.ТекущиеДанные;
    Если ТекущаяСтрокаФормы = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Ищем строку-оригинал в исходной таблице по Ключу Связи
    ПараметрыПоиска = Новый Структура("КлючСвязи", ТекущаяСтрокаФормы.КлючСвязи);
    НайденныеСтроки = тзИсходная.НайтиСтроки(ПараметрыПоиска);
    
    Если НайденныеСтроки.Количество() > 0 Тогда
        // Записываем измененные на форме данные обратно в оригинал
        ЗаполнитьЗначенияСвойств(НайденныеСтроки[0], ТекущаяСтрокаФормы);
    КонецЕсли;
    
КонецПроцедуры

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

Способ 3. Использование "Построителя запроса" для реализации сложных отборов

Стандартный метод таблиц значений НайтиСтроки() выполняет поиск только по точному равенству. Что делать, если вам требуется сложный отбор (например, вхождение в список значений, поиск по строковой подстроке "содержит", сравнение "больше/меньше")? В этом случае на помощь приходит встроенный объект платформы — ПостроительЗапроса.

Рассмотрим, как с его помощью можно быстро фильтровать нетипизированную таблицу значений:


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

Важное примечание: при использовании Способа 3 также потребуется организовать обратную запись данных в исходную таблицу при их редактировании (как описано в Шаге 4 Способа 2), поскольку построитель запроса при выгрузке результата создает новые копии строк в памяти.

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

← На главную