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

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

В процессе разработки интерфейсов на управляемых формах часто возникает задача: ограничить выбор пользователя в одной колонке табличной части в зависимости от того, что выбрано в другой. Классический пример — выбор номера строки или конкретного аналитического разреза из документа-основания, ссылка на который уже указана в текущей строке. В этой статье мы подробно разберем, как реализовать такой механизм, проанализируем типичные ошибки и выберем оптимальный способ взаимодействия клиента и сервера.

Постановка задачи и анализ ситуации

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

Разберем основные инструменты, которые предоставляет платформа 1С для решения этой задачи. Нам понадобятся события элементов формы и работа с коллекцией СписокЗначений.

Шаг 1. Получение данных на сервере

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


&НаСервере
Функция ПолучитьСписокНомеровНаСервере(ДокументСсылка)
    
    СписокНомеров = Новый Массив;
    
    // Если документ не выбран, возвращаем пустой массив
    Если ДокументСсылка.Пустая() Тогда
        Возврат СписокНомеров;
    КонецЕсли;
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ РАЗЛИЧНЫЕ
        |    ЗаказТовары.НомерДобавления КАК Номер
        |ИЗ
        |    Документ.ПредварительныйЗаказПоставщику.Товары КАК ЗаказТовары
        |ГДЕ
        |    ЗаказТовары.Ссылка = &Ссылка";
    
    Запрос.УстановитьПараметр("Ссылка", ДокументСсылка);
    
    РезультатЗапроса = Запрос.Выполнить();
    
    Если НЕ РезультатЗапроса.Пустой() Тогда
        Выборка = РезультатЗапроса.Выбрать();
        Пока Выборка.Следующий() Цикл
            СписокНомеров.Добавить(Выборка.Номер);
        КонецЦикла;
    КонецЕсли;
    
    Возврат СписокНомеров;
    
КонецФункции

Обратите внимание на использование слова РАЗЛИЧНЫЕ в тексте запроса. Это гарантирует, что если один и тот же номер встречается в исходном документе несколько раз, в наш список выбора он попадет в единственном экземпляре.

Шаг 2. Настройка элемента формы в конфигураторе

Чтобы программный код сработал корректно, необходимо правильно настроить свойства поля в табличной части на форме документа:

  1. Найдите нужную колонку (поле ввода) в дереве элементов формы.
  2. В палитре свойств установите флаг Режим выбора из списка (ChoiceListMode).
  3. Установите свойство Кнопка выпадающего списка в значение Да, чтобы пользователь видел стрелочку выбора.
  4. Создайте обработчик события НачалоВыбора (StartChoice).

Шаг 3. Реализация обработчика события НачалоВыбора

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


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

КонецПроцедуры

Важный нюанс: тип данных в массиве, который возвращает сервер, должен строго совпадать с типом реквизита в колонке Номер. Если реквизит имеет тип Число, а запрос возвращает Строку, выбор не будет зафиксирован в поле.

Шаг 4. Анализ альтернативных событий

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

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

Оптимизация: Кэширование данных (продвинутый уровень)

Вместо того чтобы запрашивать данные при каждом нажатии, мы можем подготовить их заранее — в момент изменения основной колонки ПредварительныйЗаказПоставщику. Посмотрим, как это реализовать:

  1. Создадим на форме реквизит типа Соответствие (или скрытую колонку в ТЧ с типом Произвольный).
  2. В событии ПриИзменении первой колонки вызываем сервер, получаем массив номеров и сохраняем его в кэш.
  3. В событии НачалоВыбора второй колонки просто берем данные из кэша на клиенте.

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

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

Типичные ошибки разработчиков

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

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

← На главную