Как программно реализовать выбор значения справочника в табличную часть управляемой формы 1С

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

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

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

Анализ неэффективного кода и разбор типичных ошибок

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


&НаСервере
Функция ПолучитьСпр()
    Выборка = Справочники.ФизическиеЛица.Выбрать();
    Пока Выборка.Следующий() Цикл
        Перемен = Выборка.Наименование;
    КонецЦикла;
    Возврат Перемен;
КонецФункции

&НаКлиенте
Процедура ИсторияИзмененияПриИзменении(Элемент)
    Стр = Элементы.ИсторияИзменения.ТекущиеДанные;
    Стр.Ответственный = ПолучитьСпр();
КонецПроцедуры

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

  1. Цикл перезаписи переменной. Внутри серверной функции ПолучитьСпр в цикле Пока Выборка.Следующий() переменной Перемен каждый раз присваивается новое значение. В итоге цикл просто перезаписывает данные, и функция возвращает наименование самого последнего элемента справочника.
  2. Возврат строки вместо ссылки. Функция возвращает текстовое наименование (Выборка.Наименование), а не ссылку на элемент справочника (Выборка.Ссылка). Если реквизит табличной части имеет тип ссылки, запись строки приведет к ошибке или очистке поля.
  3. Неэффективное использование ресурсов сервера. Выборка всего справочника через метод Выбрать() только ради получения одного значения — крайне тяжелая операция для базы данных, особенно если в справочнике тысячи записей.
  4. Контекст события. Событие ПриИзменении срабатывает уже после того, как пользователь изменил значение в поле. Использовать его для инициации выбора значения логически неверно.

Теперь рассмотрим, как решить задачу изящно и в соответствии со стандартами разработки «1С».

Решение №1: Программное открытие формы выбора с автоматическим заполнением (Best Practice)

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

Выполним настройку по шагам:

Шаг 1. В свойствах элемента формы (поля ввода в табличной части) находим событие НачалоВыбора и создаем на клиенте процедуру-обработчик.

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

Шаг 3. Пишем клиентский код перехвата события:


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

Важный нюанс: передача третьего параметра (Элемент) в метод ОткрытьФорму связывает открываемое диалоговое окно с нашим полем в табличной части. Когда пользователь выберет физическое лицо, платформа 1С автоматически закроет форму выбора и сама запишет выбранную ссылку в текущую строку табличной части. Нам не нужно писать ни одной строчки серверного кода!

Решение №2: Программное открытие формы с ручной обработкой результата

Иногда перед тем, как записать выбранное значение в табличную часть, требуется выполнить дополнительные проверки на клиенте (например, проверить, не выбран ли уже этот ответственный в другой строке табличной части). В этом случае автоматическая подстановка не подойдет. Нам понадобится асинхронный механизм оповещения.

Рассмотрим пример реализации:


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

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

Решение №3: Открытие формы выбора с предварительным отбором элементов

В реальных задачах часто требуется показывать пользователю не весь справочник «Физические лица», а только определенный срез данных. Например, только тех сотрудников, которые не помечены на удаление. Мы можем легко передать параметры отбора при открытии формы:


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

Решение №4: Программное ограничение типов на сервере

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

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


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

После применения данного свойства платформа сама настроит поле ввода: у него автоматически появится кнопка выбора, а при нажатии на нее откроется список справочника «Физические лица» без необходимости написания какого-либо кода в клиентских процедурах.

Осторожно: Антипаттерны динамического создания реквизитов

В обсуждении на форуме (сообщение 21) был предложен вариант полного программного пересоздания структуры реквизитов формы и элементов интерфейса через метод ИзменитьРеквизиты на сервере.

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

Для всех стандартных задач изменения типов и организации интерактивного ввода всегда отдавайте предпочтение штатным свойствам элементов формы (таким как ОграничениеТипа, ВыборГруппИЭлементов) и правильному перенаправлению владельца формы в методе ОткрытьФорму.

← На главную