Обновление данных динамического списка: код, события и автообновление
Содержание
Чтобы обновить данные динамического списка в 1С 8.3, вызовите на клиенте Элементы.Список.Обновить() или глобальный метод ОповеститьОбИзменении(Ссылка). Первый перечитывает конкретную таблицу формы, второй — все открытые списки, где показан объект этого типа. Пользователю без кода хватает команды «Обновить» в меню Ещё над списком.
Типичная боль разработчика: обработка записала документы на сервере, а журнал у бухгалтера показывает старые суммы, пока тот не переоткроет форму. Ниже — как это лечится правильно и почему список «не видит» изменения.
Как устроен динамический список и почему он не обновляется сам
Динамический список — это реквизит формы типа ДинамическийСписок. Данные в нём не хранятся. Платформа держит только описание: основную таблицу или текст запроса, отборы, сортировку, параметры. Строки она читает с сервера порциями — ровно столько, сколько помещается на экране, плюс небольшой запас.
Отсюда главное свойство: список показывает снимок на момент последнего чтения. Новая порция подтягивается, когда пользователь листает список, меняет отбор или сортировку. Если данные изменились в базе в другом месте, форма об этом не знает.
Сама платформа перечитывает список в нескольких случаях:
- ✅ пользователь записал объект в его собственной форме — платформа сама оповещает открытые списки этого типа;
- ✅ изменились отбор, сортировка, группировка или параметр запроса;
- ✅ пользователь пролистал список до границы прочитанной порции;
- ✅ у таблицы формы включено автообновление и прошёл заданный интервал.
Не перечитывает список:
- ❌ запись объекта в серверном коде (обработка, загрузка, регламентное задание);
- ❌ изменения из фонового задания;
- ❌ правки, которые сделал другой пользователь в своём сеансе;
- ❌ запись в регистр из кода другой формы без оповещения.
Именно эти четыре случая и закрывают программные способы ниже.
Ручное обновление списка через интерфейс формы
Самый простой путь — для пользователя. В управляемой форме с интерфейсом «Такси» над любым динамическим списком есть командная панель. Команда «Обновить» лежит в меню Ещё; горячая клавиша показана справа от названия команды.
Что происходит при нажатии:
- Клиент отправляет на сервер запрос с текущими отбором и сортировкой.
- Сервер читает видимую порцию строк.
- Курсор остаётся на той же записи, если она ещё попадает в выборку.
Если команды «Обновить» в меню нет, её убрали в настройках командной панели формы. Разработчик возвращает её в конфигураторе: форма → элемент таблицы → командная панель, стандартная команда Обновить.
Перед тем как писать код, проверьте вручную: если команда «Обновить» показывает свежие данные, проблема только в том, что никто не вызвал перечитывание. Если и после неё данных нет — смотрите отбор, параметры запроса и права доступа.
Программное обновление: Обновить(), ОповеститьОбИзменении и параметры
В коде есть три рабочих инструмента. Выбор зависит от того, сколько форм нужно освежить.
| Способ | Что обновляет | Где вызывать | Когда подходит |
|---|---|---|---|
Элементы.Список.Обновить() | одну таблицу в текущей форме | клиент | данные изменила сама форма |
ОповеститьОбИзменении(Ссылка или Тип) | все открытые списки с этим типом | клиент | изменения нужно показать в других окнах |
Оповестить("Событие", Параметр, Источник) | формы с обработчиком ОбработкаОповещения | клиент | нужна своя логика реакции |
| Изменение параметра или отбора | список, где изменили настройку | клиент или сервер | меняется сама выборка |
Метод Обновить() таблицы формы
Метод принадлежит элементу формы, а не реквизиту. Вызывается на клиенте:
&НаКлиенте
Процедура ПровестиВыделенные(Команда)
Ссылки = Новый Массив;
Для Каждого Строка Из Элементы.Список.ВыделенныеСтроки Цикл
Ссылки.Добавить(Строка);
КонецЦикла;
ПровестиНаСервере(Ссылки);
Элементы.Список.Обновить();
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ПровестиНаСервере(Ссылки)
Для Каждого Ссылка Из Ссылки Цикл
Объект = Ссылка.ПолучитьОбъект();
Объект.Записать(РежимЗаписиДокумента.Проведение);
КонецЦикла;
КонецПроцедуры
Сначала сервер делает работу, потом клиент перечитывает таблицу. Строки списка документов — это ссылки, поэтому их можно сразу передавать в серверную процедуру.
ОповеститьОбИзменении — обновить все окна
Метод глобального контекста перечитывает каждый открытый динамический список, основная таблица которого совпадает с типом переданного значения. Ему можно передать ссылку, ключ записи регистра или тип:
&НаКлиенте
Процедура ПослеЗагрузки()
// одна изменённая позиция
ОповеститьОбИзменении(ТекущаяНоменклатура);
// массовая загрузка: обновить все списки номенклатуры разом
ОповеститьОбИзменении(Тип("СправочникСсылка.Номенклатура"));
// регистр сведений
ОповеститьОбИзменении(Тип("РегистрСведенийКлючЗаписи.КурсыВалют"));
КонецПроцедуры
После загрузки тысячи элементов не вызывайте метод в цикле тысячу раз. Одного вызова с типом достаточно — списки перечитаются один раз.
Метод работает только на клиенте. В серверной процедуре его нет. Верните с сервера признак «были изменения» или массив ссылок и вызовите оповещение после возврата на клиент.
Связанные данные после записи в форме объекта
Платформа сама оповещает списки о записи того объекта, форма которого открыта. Но часто форма пишет ещё что-то: запись регистра сведений, подчинённый справочник, статус в отдельной таблице. Об этих данных списки не узнают.
Пример: в форме договора при записи сохраняется ставка в регистр сведений
. Список ставок, открытый в соседнем окне, останется прежним. Решение — оповестить о втором типе в событии ПослеЗаписи:
&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
ОповеститьОбИзменении(Тип("РегистрСведенийКлючЗаписи.СтавкиПоДоговорам"));
КонецПроцедуры
Событие ПослеЗаписи срабатывает на клиенте, когда запись на сервере уже завершилась. Поэтому список прочитает уже зафиксированные данные, а не промежуточное состояние.
То же касается формы, которую вы открыли из списка через ОткрытьФорму и где пользователь меняет данные обработкой. Передайте в ней Оповестить с понятным именем события при закрытии — и список-владелец обновится в ОбработкаОповещения.
Параметры и отборы — обновление выборки
Если меняется не содержимое базы, а условие выборки, отдельный вызов Обновить() не нужен. Список перечитается сам после изменения параметра:
&НаКлиенте
Процедура ДатаОтчетаПриИзменении(Элемент)
Список.Параметры.УстановитьЗначениеПараметра("ДатаСреза", ДатаОтчета);
КонецПроцедуры
В типовых конфигурациях на БСП для отборов удобна функция ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка. Она не плодит дубли элементов отбора при повторном вызове.
События формы и автоматическая синхронизация списков
Когда одна форма должна узнать о действиях другой, используйте связку Оповестить → ОбработкаОповещения. Это универсальный механизм: событие получают все открытые формы.
Отправитель — например, форма обработки после закрытия смены:
&НаКлиенте
Процедура ЗакрытьСмену(Команда)
ЗакрытьСменуНаСервере();
Оповестить("СменаЗакрыта", Объект.КассаККМ, ЭтотОбъект);
КонецПроцедуры
Получатель — форма списка чеков:
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если ИмяСобытия = "СменаЗакрыта" Тогда
Элементы.Список.Обновить();
КонецЕсли;
КонецПроцедуры
Проверяйте имя события всегда. Иначе список будет перечитываться на каждое оповещение в системе, а их в типовых конфигурациях десятки.
Автообновление по таймеру
У таблицы формы, связанной с динамическим списком, есть свойства Автообновление и ПериодАвтообновления (в секундах). Их задают в палитре свойств элемента или из кода при создании формы:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Элементы.Список.Автообновление = Истина;
Элементы.Список.ПериодАвтообновления = 60;
КонецПроцедуры
Подходит для «табло»: очередь заказов на складе, статусы обмена, задачи диспетчера. Изменения других пользователей появятся без нажатий.
Альтернатива с гибкой логикой — обработчик ожидания. Он позволяет обновлять список только при условии, например если форма активна:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
ПодключитьОбработчикОжидания("ОбновитьОчередь", 30);
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьОчередь()
Элементы.Список.Обновить();
КонецПроцедуры
Фоновые задания
Длительную операцию обычно запускают в фоне. Список нельзя обновить из самого задания: у него нет клиента. Схема такая:
- Клиент запускает задание и подключает проверку его состояния (в БСП — через
ДлительныеОперацииКлиент.ОжидатьЗавершение). - В процедуре-оповещении о завершении клиент вызывает
ОповеститьОбИзменении(Тип(...))илиЭлементы.Список.Обновить().
Производительность при обновлении больших списков
Каждое перечитывание — запрос к серверу и СУБД. На списке документов в сотни тысяч строк неудачные настройки дают заметные паузы у всех пользователей.
Что ускоряет обновление:
- 🚀 Динамическое считывание данных. Свойство реквизита
ДинамическоеСчитываниеДанныхдолжно быть включено. Тогда читается только видимая порция. Без него при каждом обновлении сервер выбирает всю таблицу. - 🚀 Основная таблица при произвольном запросе. Если список построен на своём тексте запроса, укажите
ОсновнаяТаблица. Без неё платформа хуже оптимизирует чтение порций, аОповеститьОбИзменениине сможет сопоставить список с типом объекта. - 🚀 Индексы под сортировку и отбор. Сортировка по неиндексированному реквизиту на большой таблице заставляет СУБД сортировать всё целиком. Добавьте индексирование реквизиту в конфигураторе или сортируйте по дате и номеру.
- 🚀 Лёгкий запрос. Вложенные запросы, соединения с виртуальными таблицами остатков и обороты без периода выполняются при каждом перечитывании. Выносите тяжёлое в отдельный регистр или реквизит.
Что тормозит:
- 🐢 короткий
ПериодАвтообновленияу формы, которую держат открытой десятки пользователей: 20 сеансов с периодом 5 секунд — это 240 запросов в минуту; - 🐢 тяжёлый код в событии
ПриПолученииДанныхНаСервере— он выполняется для каждой порции при каждом обновлении; - 🐢 вызов
ОповеститьОбИзменениив цикле по каждой записанной ссылке; - 🐢 сложные ограничения доступа на уровне записей: они добавляются в каждый запрос списка.
Проверить, сколько стоит одно обновление, удобно замером производительности в конфигураторе или технологическим журналом. Смотрите длительность запроса, который появляется после нажатия «Обновить».
Не ставьте период автообновления меньше 30 секунд на формах, которые открыты у многих пользователей одновременно — нагрузку почувствуют все, включая тех, кто этот список не открывал.
Типичные ошибки при обновлении динамического списка
Ещё одна частая ловушка — позиция курсора. После программной загрузки пользователь хочет видеть новую запись. Установите её явно после обновления:
Элементы.Список.Обновить();
Элементы.Список.ТекущаяСтрока = НоваяСсылка;
Если запись не попала в выборку из-за отбора, курсор не переместится — это не ошибка кода.
Если список всё равно показывает старые данные
Когда и ручное «Обновить», и код не помогают, причина обычно не в форме:
- Транзакция ещё не завершена. Если запись идёт внутри длинной транзакции, другие сеансы видят старые данные до её фиксации. Проверьте, где вызывается
ЗафиксироватьТранзакцию(). - Данные пишутся в другую таблицу. Список выводит регистр, а код меняет документ без перепроведения — движения не обновились.
- Кэш на клиенте. Подписи и представления ссылок кэшируются. Помогает перезапуск клиента; для тонкого клиента можно запустить базу с параметром
/ClearCache.
Перезапуск с очисткой кэша закроет текущий сеанс пользователя. Предупредите его и сохраните открытые документы заранее.