Обновление данных динамического списка: код, события и автообновление

· 9 мин

Содержание

Чтобы обновить данные динамического списка в 1С 8.3, вызовите на клиенте Элементы.Список.Обновить() или глобальный метод ОповеститьОбИзменении(Ссылка). Первый перечитывает конкретную таблицу формы, второй — все открытые списки, где показан объект этого типа. Пользователю без кода хватает команды «Обновить» в меню Ещё над списком.

Типичная боль разработчика: обработка записала документы на сервере, а журнал у бухгалтера показывает старые суммы, пока тот не переоткроет форму. Ниже — как это лечится правильно и почему список «не видит» изменения.

Как устроен динамический список и почему он не обновляется сам

Динамический список — это реквизит формы типа ДинамическийСписок. Данные в нём не хранятся. Платформа держит только описание: основную таблицу или текст запроса, отборы, сортировку, параметры. Строки она читает с сервера порциями — ровно столько, сколько помещается на экране, плюс небольшой запас.

Отсюда главное свойство: список показывает снимок на момент последнего чтения. Новая порция подтягивается, когда пользователь листает список, меняет отбор или сортировку. Если данные изменились в базе в другом месте, форма об этом не знает.

Сама платформа перечитывает список в нескольких случаях:

  • ✅ пользователь записал объект в его собственной форме — платформа сама оповещает открытые списки этого типа;
  • ✅ изменились отбор, сортировка, группировка или параметр запроса;
  • ✅ пользователь пролистал список до границы прочитанной порции;
  • ✅ у таблицы формы включено автообновление и прошёл заданный интервал.

Не перечитывает список:

  • ❌ запись объекта в серверном коде (обработка, загрузка, регламентное задание);
  • ❌ изменения из фонового задания;
  • ❌ правки, которые сделал другой пользователь в своём сеансе;
  • ❌ запись в регистр из кода другой формы без оповещения.

Именно эти четыре случая и закрывают программные способы ниже.

Ручное обновление списка через интерфейс формы

Самый простой путь — для пользователя. В управляемой форме с интерфейсом «Такси» над любым динамическим списком есть командная панель. Команда «Обновить» лежит в меню Ещё; горячая клавиша показана справа от названия команды.

Что происходит при нажатии:

  1. Клиент отправляет на сервер запрос с текущими отбором и сортировкой.
  2. Сервер читает видимую порцию строк.
  3. Курсор остаётся на той же записи, если она ещё попадает в выборку.

Если команды «Обновить» в меню нет, её убрали в настройках командной панели формы. Разработчик возвращает её в конфигураторе: форма → элемент таблицы → командная панель, стандартная команда Обновить.

Перед тем как писать код, проверьте вручную: если команда «Обновить» показывает свежие данные, проблема только в том, что никто не вызвал перечитывание. Если и после неё данных нет — смотрите отбор, параметры запроса и права доступа.

Программное обновление: Обновить(), ОповеститьОбИзменении и параметры

В коде есть три рабочих инструмента. Выбор зависит от того, сколько форм нужно освежить.

СпособЧто обновляетГде вызыватьКогда подходит
Элементы.Список.Обновить()одну таблицу в текущей формеклиентданные изменила сама форма
ОповеститьОбИзменении(Ссылка или Тип)все открытые списки с этим типомклиентизменения нужно показать в других окнах
Оповестить("Событие", Параметр, Источник)формы с обработчиком ОбработкаОповещенияклиентнужна своя логика реакции
Изменение параметра или отборасписок, где изменили настройкуклиент или серверменяется сама выборка

Метод Обновить() таблицы формы

Метод принадлежит элементу формы, а не реквизиту. Вызывается на клиенте:

&НаКлиенте
Процедура ПровестиВыделенные(Команда)

    Ссылки = Новый Массив;
    Для Каждого Строка Из Элементы.Список.ВыделенныеСтроки Цикл
        Ссылки.Добавить(Строка);
    КонецЦикла;

    ПровестиНаСервере(Ссылки);
    Элементы.Список.Обновить();

КонецПроцедуры

&НаСервереБезКонтекста
Процедура ПровестиНаСервере(Ссылки)

    Для Каждого Ссылка Из Ссылки Цикл
        Объект = Ссылка.ПолучитьОбъект();
        Объект.Записать(РежимЗаписиДокумента.Проведение);
    КонецЦикла;

КонецПроцедуры

Сначала сервер делает работу, потом клиент перечитывает таблицу. Строки списка документов — это ссылки, поэтому их можно сразу передавать в серверную процедуру.

ОповеститьОбИзменении — обновить все окна

Метод глобального контекста перечитывает каждый открытый динамический список, основная таблица которого совпадает с типом переданного значения. Ему можно передать ссылку, ключ записи регистра или тип:

&НаКлиенте
Процедура ПослеЗагрузки()

    // одна изменённая позиция
    ОповеститьОбИзменении(ТекущаяНоменклатура);

    // массовая загрузка: обновить все списки номенклатуры разом
    ОповеститьОбИзменении(Тип("СправочникСсылка.Номенклатура"));

    // регистр сведений
    ОповеститьОбИзменении(Тип("РегистрСведенийКлючЗаписи.КурсыВалют"));

КонецПроцедуры

После загрузки тысячи элементов не вызывайте метод в цикле тысячу раз. Одного вызова с типом достаточно — списки перечитаются один раз.

Метод работает только на клиенте. В серверной процедуре его нет. Верните с сервера признак «были изменения» или массив ссылок и вызовите оповещение после возврата на клиент.

Связанные данные после записи в форме объекта

Платформа сама оповещает списки о записи того объекта, форма которого открыта. Но часто форма пишет ещё что-то: запись регистра сведений, подчинённый справочник, статус в отдельной таблице. Об этих данных списки не узнают.

Пример: в форме договора при записи сохраняется ставка в регистр сведений . Список ставок, открытый в соседнем окне, останется прежним. Решение — оповестить о втором типе в событии ПослеЗаписи:

&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)

    ОповеститьОбИзменении(Тип("РегистрСведенийКлючЗаписи.СтавкиПоДоговорам"));

КонецПроцедуры

Событие ПослеЗаписи срабатывает на клиенте, когда запись на сервере уже завершилась. Поэтому список прочитает уже зафиксированные данные, а не промежуточное состояние.

То же касается формы, которую вы открыли из списка через ОткрытьФорму и где пользователь меняет данные обработкой. Передайте в ней Оповестить с понятным именем события при закрытии — и список-владелец обновится в ОбработкаОповещения.

Параметры и отборы — обновление выборки

Если меняется не содержимое базы, а условие выборки, отдельный вызов Обновить() не нужен. Список перечитается сам после изменения параметра:

&НаКлиенте
Процедура ДатаОтчетаПриИзменении(Элемент)

    Список.Параметры.УстановитьЗначениеПараметра("ДатаСреза", ДатаОтчета);

КонецПроцедуры

В типовых конфигурациях на БСП для отборов удобна функция ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка. Она не плодит дубли элементов отбора при повторном вызове.

События формы и автоматическая синхронизация списков

Когда одна форма должна узнать о действиях другой, используйте связку Оповестить → ОбработкаОповещения. Это универсальный механизм: событие получают все открытые формы.

Отправитель — например, форма обработки после закрытия смены:

&НаКлиенте
Процедура ЗакрытьСмену(Команда)

    ЗакрытьСменуНаСервере();
    Оповестить("СменаЗакрыта", Объект.КассаККМ, ЭтотОбъект);

КонецПроцедуры

Получатель — форма списка чеков:

&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)

    Если ИмяСобытия = "СменаЗакрыта" Тогда
        Элементы.Список.Обновить();
    КонецЕсли;

КонецПроцедуры

Проверяйте имя события всегда. Иначе список будет перечитываться на каждое оповещение в системе, а их в типовых конфигурациях десятки.

Автообновление по таймеру

У таблицы формы, связанной с динамическим списком, есть свойства Автообновление и ПериодАвтообновления (в секундах). Их задают в палитре свойств элемента или из кода при создании формы:

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)

    Элементы.Список.Автообновление = Истина;
    Элементы.Список.ПериодАвтообновления = 60;

КонецПроцедуры

Подходит для «табло»: очередь заказов на складе, статусы обмена, задачи диспетчера. Изменения других пользователей появятся без нажатий.

Альтернатива с гибкой логикой — обработчик ожидания. Он позволяет обновлять список только при условии, например если форма активна:

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    ПодключитьОбработчикОжидания("ОбновитьОчередь", 30);
КонецПроцедуры

&НаКлиенте
Процедура ОбновитьОчередь()
    Элементы.Список.Обновить();
КонецПроцедуры

Фоновые задания

Длительную операцию обычно запускают в фоне. Список нельзя обновить из самого задания: у него нет клиента. Схема такая:

  1. Клиент запускает задание и подключает проверку его состояния (в БСП — через ДлительныеОперацииКлиент.ОжидатьЗавершение).
  2. В процедуре-оповещении о завершении клиент вызывает ОповеститьОбИзменении(Тип(...)) или Элементы.Список.Обновить().
Какой способ обновления вам нужен?

Производительность при обновлении больших списков

Каждое перечитывание — запрос к серверу и СУБД. На списке документов в сотни тысяч строк неудачные настройки дают заметные паузы у всех пользователей.

Что ускоряет обновление:

  • 🚀 Динамическое считывание данных. Свойство реквизита ДинамическоеСчитываниеДанных должно быть включено. Тогда читается только видимая порция. Без него при каждом обновлении сервер выбирает всю таблицу.
  • 🚀 Основная таблица при произвольном запросе. Если список построен на своём тексте запроса, укажите ОсновнаяТаблица. Без неё платформа хуже оптимизирует чтение порций, а ОповеститьОбИзменении не сможет сопоставить список с типом объекта.
  • 🚀 Индексы под сортировку и отбор. Сортировка по неиндексированному реквизиту на большой таблице заставляет СУБД сортировать всё целиком. Добавьте индексирование реквизиту в конфигураторе или сортируйте по дате и номеру.
  • 🚀 Лёгкий запрос. Вложенные запросы, соединения с виртуальными таблицами остатков и обороты без периода выполняются при каждом перечитывании. Выносите тяжёлое в отдельный регистр или реквизит.

Что тормозит:

  • 🐢 короткий ПериодАвтообновления у формы, которую держат открытой десятки пользователей: 20 сеансов с периодом 5 секунд — это 240 запросов в минуту;
  • 🐢 тяжёлый код в событии ПриПолученииДанныхНаСервере — он выполняется для каждой порции при каждом обновлении;
  • 🐢 вызов ОповеститьОбИзменении в цикле по каждой записанной ссылке;
  • 🐢 сложные ограничения доступа на уровне записей: они добавляются в каждый запрос списка.

Проверить, сколько стоит одно обновление, удобно замером производительности в конфигураторе или технологическим журналом. Смотрите длительность запроса, который появляется после нажатия «Обновить».

Не ставьте период автообновления меньше 30 секунд на формах, которые открыты у многих пользователей одновременно — нагрузку почувствуют все, включая тех, кто этот список не открывал.

Типичные ошибки при обновлении динамического списка

Диагностика: выберите симптом

Ещё одна частая ловушка — позиция курсора. После программной загрузки пользователь хочет видеть новую запись. Установите её явно после обновления:

Элементы.Список.Обновить();
Элементы.Список.ТекущаяСтрока = НоваяСсылка;

Если запись не попала в выборку из-за отбора, курсор не переместится — это не ошибка кода.

Если список всё равно показывает старые данные

Когда и ручное «Обновить», и код не помогают, причина обычно не в форме:

  1. Транзакция ещё не завершена. Если запись идёт внутри длинной транзакции, другие сеансы видят старые данные до её фиксации. Проверьте, где вызывается ЗафиксироватьТранзакцию().
  2. Данные пишутся в другую таблицу. Список выводит регистр, а код меняет документ без перепроведения — движения не обновились.
  3. Кэш на клиенте. Подписи и представления ссылок кэшируются. Помогает перезапуск клиента; для тонкого клиента можно запустить базу с параметром /ClearCache.

Перезапуск с очисткой кэша закроет текущий сеанс пользователя. Предупредите его и сохраните открытые документы заранее.

Частые вопросы

Можно ли обновить динамический список из модуля объекта?
Нет. Модуль объекта выполняется на сервере и о формах не знает. Обновление вызывают на клиенте той формы, которая инициировала запись.
Обновляет ли ОповеститьОбИзменении списки у других пользователей?
Нет. Метод действует только в текущем сеансе. Чтобы другие пользователи увидели изменения, нужно автообновление таблицы формы или обработчик ожидания.
Чем Обновить() отличается от переоткрытия формы?
Обновить() перечитывает только данные и сохраняет отборы, сортировку и позицию. Переоткрытие пересоздаёт форму целиком и вызывает ПриСозданииНаСервере заново.
Нужно ли обновлять список после изменения отбора из кода?
Нет. Изменение отбора, сортировки или параметра само запускает перечитывание.
Работает ли автообновление в веб-клиенте?
Да, свойства Автообновление и ПериодАвтообновления таблицы формы действуют и в тонком, и в веб-клиенте.