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