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