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