Динамический список не видит новые записи: обновляем его из кода

· 9 мин

Содержание

Чтобы программно обновить динамический список в управляемой форме 1С 8.3, вызовите на клиенте Элементы.Список.Обновить(). Если данные поменялись в другой форме или в серверной процедуре, удобнее ОповеститьОбИзменении(Ссылка): платформа сама перечитает все открытые списки с объектами этого типа.

Типовая боль: обработка создала документы, а в списке их нет, пока пользователь не нажмёт F5 или не переоткроет форму. Причина почти всегда одна — код записал данные в базу, но никто не сказал форме, что их пора перечитать. Это решается одной-двумя строками на клиенте, если понимать, какой механизм за что отвечает.

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

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

Способов несколько, и они не взаимозаменяемы:

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

Если коротко: правка из своей формы — Обновить(), правка где-то ещё — ОповеститьОбИзменении, правка в чужом сеансе — автообновление или таймер.

Метод Обновить() у элемента формы и аналог «Перечитать»

Обновить() — метод элемента формы ТаблицаФормы, а не реквизита. Вызывается он только на клиенте:

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

Здесь Список — имя элемента таблицы на форме. Если у вас элемент называется СписокДокументов, пишите Элементы.СписокДокументов.Обновить().

Метода Перечитать() у динамического списка в управляемой форме нет. Его роль выполняет тот же Обновить(): он сбрасывает закешированные порции и запрашивает видимые строки заново. Текущая строка и выделение по возможности сохраняются.

Не путайте с похожими методами:

  • 🔹 Прочитать() — метод формы объекта. Перечитывает реквизит Объект из базы, к спискам отношения не имеет.
  • 🔹 ОбновитьОтображениеДанных() — перерисовывает данные формы. Нового запроса к базе для динамического списка он не вызывает.
  • 🔹 ОбновитьИнтерфейс() — перестраивает командный интерфейс. Для списков бесполезен и заметно тормозит клиент.

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

&НаКлиенте
Процедура ГрупповаяПравка(Команда)
	Оповещение = Новый ОписаниеОповещения("ГрупповаяПравкаЗавершение", ЭтотОбъект);
	ОткрытьФорму("Обработка.ГрупповаяПравка.Форма", , ЭтотОбъект, , , , Оповещение);
КонецПроцедуры

&НаКлиенте
Процедура ГрупповаяПравкаЗавершение(Результат, ДополнительныеПараметры) Экспорт
	Элементы.Список.Обновить();
КонецПроцедуры

Процедура-обработчик обязательно экспортная, иначе платформа её не вызовет.

Вызывайте Обновить() один раз в конце операции. Вызов внутри цикла после каждой записи заставит платформу гонять запрос столько раз, сколько итераций.

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

Когда объект записан из своей стандартной формы, открытые списки платформа обновляет сама. Проблема появляется, когда запись идёт кодом: из обработки, из серверной процедуры, из формы другого объекта.

Для этого есть ОповеститьОбИзменении. Ему можно передать ссылку или тип:

&НаКлиенте
Процедура ПослеЗаписиНаСервере(СозданныеДокументы)
	
	// Вариант 1: по конкретной ссылке
	Для Каждого Документ Из СозданныеДокументы Цикл
		ОповеститьОбИзменении(Документ);
	КонецЦикла;
	
	// Вариант 2: сразу по типу, если записей много
	ОповеститьОбИзменении(Тип("ДокументСсылка.РеализацияТоваровУслуг"));
	
КонецПроцедуры

Сам метод — клиентский. Сервер ничего «оповестить» не может, поэтому схема всегда одна: сервер возвращает ссылки на клиент, клиент вызывает оповещение.

Если открытой форме нужно не только перечитать список, но и, например, пересчитать итог в шапке, используйте пару Оповестить и ОбработкаОповещения:

// В обработке, создавшей документы
&НаКлиенте
Процедура СоздатьИОповестить(Команда)
	СоздатьДокументыНаСервере();
	Оповестить("ЗагрузкаРеализацийЗавершена", , ЭтотОбъект);
КонецПроцедуры

// В форме списка
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
	Если ИмяСобытия = "ЗагрузкаРеализацийЗавершена" Тогда
		Элементы.Список.Обновить();
		ОбновитьИтогиНаСервере();
	КонецЕсли;
КонецПроцедуры

Имя события — произвольная строка. Договоритесь о нём один раз и держите в одном месте, иначе опечатка в одной из форм молча сломает обновление.

Обновление динамического списка через параметры, отбор и запрос

Второй путь — изменить условия выборки. Любое изменение параметра, отбора или текста запроса платформа считает сигналом перечитать список. Отдельный Обновить() после этого не нужен.

Параметры запроса

Если в запросе списка есть &НачалоПериода и &КонецПериода, их значения задаются так:

&НаКлиенте
Процедура ПериодПриИзменении(Элемент)
	Список.Параметры.УстановитьЗначениеПараметра("НачалоПериода", НачалоДня(Период.ДатаНачала));
	Список.Параметры.УстановитьЗначениеПараметра("КонецПериода", КонецДня(Период.ДатаОкончания));
КонецПроцедуры

Начальные значения всех параметров задайте в ПриСозданииНаСервере. Незаполненный параметр приводит к ошибке при первом открытии формы.

Отбор

В конфигурациях на БСП (БП 3.0, ЗУП 3.1, УТ 11) отбор удобнее ставить общей процедурой. Она сама находит или создаёт элемент отбора:

ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
	Список, "Организация", Организация,
	ВидСравненияКомпоновкиДанных.Равно, , ЗначениеЗаполнено(Организация));

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

Текст запроса

Текст запроса меняют на сервере, у списка должен быть включён флаг ПроизвольныйЗапрос:

&НаСервере
Процедура ПереключитьРежимНаСервере()
	Список.ТекстЗапроса = ПолучитьТекстЗапроса(РежимПросмотра);
КонецПроцедуры

Менять текст ради одного условия — дорогое решение: платформа заново разбирает запрос, а пользовательские настройки, завязанные на старые поля, могут слететь. Для условий хватает параметров и отбора.

Изменения из других сеансов, фоновых заданий и обмена РИБ

ОповеститьОбИзменении и Оповестить действуют только внутри одного клиентского сеанса. Если документ записал коллега на соседнем компьютере, регламентное задание или загрузка обмена в распределённой базе, ваш список об этом не узнает.

Вариантов три.

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

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	Элементы.Список.Автообновление = Истина;
	Элементы.Список.ПериодАвтообновления = 60;
КонецПроцедуры

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

&НаКлиенте
Процедура ПроверитьФоновоеЗадание()
	Если ЗаданиеВыполнено(ИдентификаторЗадания) Тогда
		ОтключитьОбработчикОжидания("ПроверитьФоновоеЗадание");
		Элементы.Список.Обновить();
	КонецЕсли;
КонецПроцедуры

Функция ЗаданиеВыполнено здесь — ваша серверная функция, которая проверяет состояние задания. В БСП для этого есть готовые механизмы длительных операций с оповещением о завершении.

Явное обновление после обмена. Если обмен запускает сам пользователь из формы, вызовите ОповеститьОбИзменении по типам загруженных объектов в обработчике завершения.

Не ставьте период автообновления в несколько секунд на списках, которые открыты у десятков пользователей. Каждый тик — запрос к серверу от каждого клиента. Для большинства задач хватает 60–300 секунд.

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

Обновление — это повторный запрос. Если список открывается 5 секунд, то и каждое обновление займёт столько же. Ускорять нужно сам список:

  • ⚡ Включите ДинамическоеСчитываниеДанных. Тогда платформа читает данные порциями по мере прокрутки, а не весь результат сразу.
  • ⚡ Укажите основную таблицу. Без неё ключом строки будет не ссылка на объект, и позиционирование по ссылке, как в примере ниже, не сработает.
  • ⚡ Не тяните виртуальные таблицы остатков без условий. Параметры виртуальной таблицы (период, отбор по организации) ставьте в её параметрах, а не в ГДЕ.
  • ⚡ Сортируйте по индексированным полям. Сортировка по вычисляемому полю заставляет СУБД обрабатывать всю выборку.
  • ⚡ Держите лёгким ПриПолученииДанныхНаСервере. Этот обработчик вызывается на каждую порцию строк. Запрос внутри него на 50 строк — 50 лишних обращений к базе при каждом обновлении.
  • ⚡ Обновляйте один раз. Собирайте ссылки в массив, после операции вызывайте одно оповещение по типу.

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

Обычные и управляемые формы: в чём разница

Код из управляемой формы в обычной не заработает, и наоборот.

ДействиеУправляемая формаОбычная форма
Обновить списокЭлементы.Список.Обновить() на клиентеметод ОбновитьСтроки() табличного поля
Доступ к элементуЭлементы.ИмяЭлементаЭлементыФормы.ИмяЭлемента
ОтборСписок.Отбор (схема компоновки)Отбор реквизита-списка с методами старого отбора
Контекст выполненияклиент и сервер разделенывесь модуль формы выполняется в одном контексте
Оповещение других формОповеститьОбИзменении, Оповеститьтот же механизм оповещений

Главное отличие — разделение клиента и сервера. В обычной форме можно записать документ и тут же обновить список в одной процедуре. В управляемой запись идёт в серверной процедуре, обновление — в клиентской, после возврата из серверного вызова.

Если конфигурация работает в режиме управляемого приложения (БП 3.0, ЗУП 3.1, УТ 11), всё сказанное про обычные формы к ней не относится.

Пример: создаём документ на сервере и показываем его в списке

Задача: в форме списка реализаций есть кнопка «Создать по шаблону». Документ создаётся на сервере, после чего список должен обновиться, а курсор встать на новую строку.

&НаКлиенте
Процедура СоздатьПоШаблону(Команда)
	
	НовыйДокумент = СоздатьПоШаблонуНаСервере();
	
	Если Не ЗначениеЗаполнено(НовыйДокумент) Тогда
		Возврат;
	КонецЕсли;
	
	ОповеститьОбИзменении(НовыйДокумент);
	Элементы.Список.ТекущаяСтрока = НовыйДокумент;
	
КонецПроцедуры

&НаСервере
Функция СоздатьПоШаблонуНаСервере()
	
	ТекущаяСтрока = Элементы.Список.ТекущаяСтрока;
	Если ТекущаяСтрока = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Документ = ТекущаяСтрока.Скопировать();
	Документ.Дата = ТекущаяДатаСеанса();
	
	Попытка
		Документ.Записать(РежимЗаписиДокумента.Запись);
	Исключение
		ОбщегоНазначения.СообщитьПользователю(КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
		Возврат Неопределено;
	КонецПопытки;
	
	Возврат Документ.Ссылка;
	
КонецФункции

Что здесь происходит:

  1. Клиент вызывает серверную функцию. Она копирует документ под курсором и записывает копию без проведения.
  2. Сервер возвращает ссылку или Неопределено, если копировать нечего или запись не прошла.
  3. Клиент оповещает об изменении. Список перечитывается, причём и в других открытых формах этого сеанса.
  4. Присвоение ТекущаяСтрока ставит курсор на созданный документ. Для динамического списка текущая строка — это ключ, то есть ссылка.

Если на списке стоит отбор по периоду, а копия получила сегодняшнюю дату за его пределами, позиционироваться будет не на что: строка просто не попадёт в выборку. В таком случае перед присвоением ТекущаяСтрока расширьте период или снимите отбор.

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

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

Почему список не обновляется

  • ❌ Запись кодом без оповещения. Обработка записала документы, но форма об этом не знает. Новые строки появляются только после переоткрытия. Лечится возвратом ссылок на клиент и вызовом ОповеститьОбИзменении.
  • ❌ Обновление на сервере. Элементы.Список.Обновить() в процедуре &НаСервере не компилируется. На сервере можно менять параметры и отбор: список перечитается, когда управление вернётся на клиент.
  • ❌ Даты без границ дня. Параметр КонецПериода, равный дате без времени, отсекает все документы этого дня позже 00:00:00. Используйте КонецДня().
  • ❌ Документ не проходит отбор. Код создал документ с датой вне выбранного периода или по другой организации. Список обновился честно, просто строка отфильтрована. Проверьте отборы и параметры формы, прежде чем искать ошибку в коде.
  • ❌ Ожидание мгновенного обновления у коллег. Оповещения и Обновить() работают только в своём сеансе.
  • ❌ Не тот тип в оповещении. ОповеститьОбИзменении ждёт ссылку или тип ссылки: Тип("ДокументСсылка.РеализацияТоваровУслуг"). Тип объекта (ДокументОбъект…) или имя метаданных строкой списки не обновят.
  • ❌ Список с произвольным запросом без основной таблицы. Оповещение по типу не к чему привязать, и такой список остаётся прежним. Укажите основную таблицу или обновляйте его через Обновить().

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

Можно ли обновить динамический список из модуля объекта или общего модуля на сервере?
Нет. Сервер не управляет открытыми формами. Верните ссылки на клиент и вызовите ОповеститьОбИзменении там.
Сохраняются ли пользовательские настройки списка после Обновить()?
Да. Отборы, сортировка и порядок колонок остаются. Риск их потерять появляется только при смене текста запроса.
Нужно ли вызывать Обновить() после изменения отбора?
Нет. Изменение отбора или параметра само запускает перечитывание списка.
Чем ОповеститьОбИзменении по ссылке отличается от вызова по типу?
Результат одинаковый: перечитываются списки с объектами этого типа. Вызов по типу удобен, когда изменено много объектов: одна строка вместо цикла.
Какой период автообновления ставить?
Ориентировочно 60–300 секунд. Чем больше пользователей держат список открытым, тем больше период.