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