В процессе разработки интерфейсов на управляемых формах часто возникает задача: ограничить выбор пользователя в одной колонке табличной части в зависимости от того, что выбрано в другой. Классический пример — выбор номера строки или конкретного аналитического разреза из документа-основания, ссылка на который уже указана в текущей строке. В этой статье мы подробно разберем, как реализовать такой механизм, проанализируем типичные ошибки и выберем оптимальный способ взаимодействия клиента и сервера.
Представим сценарий: у нас есть табличная часть Лимиты. В первой колонке ПредварительныйЗаказПоставщику пользователь выбирает документ. Во второй колонке Номер необходимо предоставить список всех уникальных номеров, которые содержатся в табличной части Товары выбранного заказа. Проблема заключается в том, что список выбора для колонки Номер должен быть уникальным для каждой строки таблицы, так как заказы в разных строках могут быть разными.
Разберем основные инструменты, которые предоставляет платформа 1С для решения этой задачи. Нам понадобятся события элементов формы и работа с коллекцией СписокЗначений.
Прежде всего, нам нужна функция, которая по ссылке на документ быстро соберет необходимые данные из его табличной части. Использование запроса — наиболее эффективный метод, так как он позволяет избежать лишнего чтения объекта документа в память целиком. Проанализируем пример функции:
&НаСервере
Функция ПолучитьСписокНомеровНаСервере(ДокументСсылка)
СписокНомеров = Новый Массив;
// Если документ не выбран, возвращаем пустой массив
Если ДокументСсылка.Пустая() Тогда
Возврат СписокНомеров;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| ЗаказТовары.НомерДобавления КАК Номер
|ИЗ
| Документ.ПредварительныйЗаказПоставщику.Товары КАК ЗаказТовары
|ГДЕ
| ЗаказТовары.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", ДокументСсылка);
РезультатЗапроса = Запрос.Выполнить();
Если НЕ РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
СписокНомеров.Добавить(Выборка.Номер);
КонецЦикла;
КонецЕсли;
Возврат СписокНомеров;
КонецФункции
Обратите внимание на использование слова РАЗЛИЧНЫЕ в тексте запроса. Это гарантирует, что если один и тот же номер встречается в исходном документе несколько раз, в наш список выбора он попадет в единственном экземпляре.
Чтобы программный код сработал корректно, необходимо правильно настроить свойства поля в табличной части на форме документа:
ChoiceListMode).Да, чтобы пользователь видел стрелочку выбора.StartChoice).Теперь рассмотрим самый важный этап — связывание данных с интерфейсом. Мы используем событие НачалоВыбора, так как оно позволяет перехватить стандартный процесс открытия формы выбора и подменить его нашим списком. Выясним причину, по которой у многих разработчиков значение «не прилипает» к полю: это часто связано с игнорированием параметра ДанныеВыбора.
&НаКлиенте
Процедура ЛимитыНомерНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
// Отменяем стандартное открытие формы выбора
СтандартнаяОбработка = Ложь;
// Получаем данные текущей строки табличной части
ТекСтрока = Элементы.Лимиты.ТекущиеДанные;
Если ТекСтрока = Неопределено Тогда
Возврат;
КонецЕсли;
// Вызываем серверную функцию для получения массива номеров
МассивНомеров = ПолучитьСписокНомеровНаСервере(ТекСтрока.ПредварительныйЗаказПоставщику);
// Формируем список значений для отображения пользователю
СписокДляВыбора = Новый СписокЗначений;
СписокДляВыбора.ЗагрузитьЗначения(МассивНомеров);
// Заполняем параметр ДанныеВыбора — это именно то, что увидит пользователь
ДанныеВыбора = СписокДляВыбора;
// Дополнительно обновляем список выбора самого элемента для корректного отображения
Элемент.СписокВыбора.Очистить();
Для Каждого ЗначениеИзСписка Из МассивНомеров Цикл
Элемент.СписокВыбора.Добавить(ЗначениеИзСписка);
КонецЦикла;
КонецПроцедуры
Важный нюанс: тип данных в массиве, который возвращает сервер, должен строго совпадать с типом реквизита в колонке Номер. Если реквизит имеет тип Число, а запрос возвращает Строку, выбор не будет зафиксирован в поле.
Разберем ситуацию, почему событие НачалоВыбораИзСписка часто оказывается нерабочим в данном контексте. Это событие специфично для полей, где список выбора статичен или предопределен. В табличных частях, где контекст меняется от строки к строке, использование НачалоВыбора с параметром ДанныеВыбора является более надежным и гибким решением.
Также проанализируем ситуацию с производительностью. Если пользователь часто открывает список выбора, постоянные серверные вызовы в момент клика могут привести к микро-задержкам интерфейса. Чтобы этого избежать, рассмотрим метод кэширования.
Вместо того чтобы запрашивать данные при каждом нажатии, мы можем подготовить их заранее — в момент изменения основной колонки ПредварительныйЗаказПоставщику. Посмотрим, как это реализовать:
Соответствие (или скрытую колонку в ТЧ с типом Произвольный).ПриИзменении первой колонки вызываем сервер, получаем массив номеров и сохраняем его в кэш.НачалоВыбора второй колонки просто берем данные из кэша на клиенте.
&НаКлиенте
Процедура ЛимитыПредварительныйЗаказПоставщикуПриИзменении(Элемент)
ТекСтрока = Элементы.Лимиты.ТекущиеДанные;
// Получаем данные один раз и сохраняем их, например, в скрытую колонку ТЧ "КэшНомеров"
ТекСтрока.КэшНомеров = ПолучитьСписокНомеровНаСервере(ТекСтрока.ПредварительныйЗаказПоставщику);
КонецПроцедуры
&НаКлиенте
Процедура ЛимитыНомерНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
ТекСтрока = Элементы.Лимиты.ТекущиеДанные;
Если ТекСтрока <> Неопределено И ТипЗнч(ТекСтрока.КэшНомеров) = Тип("Массив") Тогда
ДанныеВыбора = Новый СписокЗначений;
ДанныеВыбора.ЗагрузитьЗначения(ТекСтрока.КэшНомеров);
КонецЕсли;
КонецПроцедуры
Рассмотрим основные грабли, на которые наступают при реализации этого функционала:
ДанныеВыбора.Элемент.СписокВыбора визуально опустошит выпадающий список, но если не передать новые значения в ДанныеВыбора в событии НачалоВыбора, список может просто не открыться.Используя предложенные методы, мы создаем отзывчивый и интуитивно понятный интерфейс для пользователя, минимизируем количество ошибок ввода и обеспечиваем корректную работу бизнес-логики документа.