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