Динамический список не видит новые записи: обновляем его из кода
Содержание
Чтобы программно обновить динамический список
в управляемой форме 1С 8.3, вызовите на клиенте Элементы.Список.Обновить(). Если данные поменялись в другой форме или в серверной процедуре, удобнее ОповеститьОбИзменении(Ссылка): платформа сама перечитает все открытые списки с объектами этого типа.
Типовая боль: обработка создала документы, а в списке их нет, пока пользователь не нажмёт F5 или не переоткроет форму. Причина почти всегда одна — код записал данные в базу, но никто не сказал форме, что их пора перечитать. Это решается одной-двумя строками на клиенте, если понимать, какой механизм за что отвечает.
Какой способ обновления динамического списка выбрать
Динамический список не хранит данные в форме. Он читает их из базы порциями, когда строки нужно показать. Поэтому «обновить» значит заставить платформу заново выполнить запрос списка.
Способов несколько, и они не взаимозаменяемы:
| Способ | Где вызывать | Когда подходит |
|---|---|---|
Элементы.Список.Обновить() | клиент, та же форма | данные изменены кодом этой формы |
ОповеститьОбИзменении(Ссылка или Тип) | клиент, любая форма | объект изменён в другой форме или на сервере |
Оповестить() + ОбработкаОповещения | клиент | нужно не только перечитать, но и выполнить свою логику |
| Изменение параметра, отбора или текста запроса | клиент или сервер | меняются условия выборки |
| Автообновление элемента формы | свойства элемента | данные меняют другие пользователи или регламентные задания |
ПодключитьОбработчикОжидания | клиент | нужна своя периодичность или условие обновления |
Если коротко: правка из своей формы — Обновить(), правка где-то ещё — ОповеститьОбИзменении, правка в чужом сеансе — автообновление или таймер.
Метод Обновить() у элемента формы и аналог «Перечитать»
Обновить() — метод элемента формы ТаблицаФормы, а не реквизита. Вызывается он только на клиенте:
&НаКлиенте
Процедура ОбновитьСписок(Команда)
Элементы.Список.Обновить();
КонецПроцедуры
Здесь Список — имя элемента таблицы на форме. Если у вас элемент называется СписокДокументов, пишите Элементы.СписокДокументов.Обновить().
Метода Перечитать() у динамического списка в управляемой форме нет. Его роль выполняет тот же Обновить(): он сбрасывает закешированные порции и запрашивает видимые строки заново. Текущая строка и выделение по возможности сохраняются.
Не путайте с похожими методами:
- 🔹
Прочитать()— метод формы объекта. Перечитывает реквизитОбъектиз базы, к спискам отношения не имеет. - 🔹
ОбновитьОтображениеДанных()— перерисовывает данные формы. Нового запроса к базе для динамического списка он не вызывает. - 🔹
ОбновитьИнтерфейс()— перестраивает командный интерфейс. Для списков бесполезен и заметно тормозит клиент.
Частый случай — обновить список после закрытия формы-диалога, например обработки групповой правки. Откройте её с описанием оповещения и обновляйте в обработчике закрытия:
&НаКлиенте
Процедура ГрупповаяПравка(Команда)
Оповещение = Новый ОписаниеОповещения("ГрупповаяПравкаЗавершение", ЭтотОбъект);
ОткрытьФорму("Обработка.ГрупповаяПравка.Форма", , ЭтотОбъект, , , , Оповещение);
КонецПроцедуры
&НаКлиенте
Процедура ГрупповаяПравкаЗавершение(Результат, ДополнительныеПараметры) Экспорт
Элементы.Список.Обновить();
КонецПроцедуры
Процедура-обработчик обязательно экспортная, иначе платформа её не вызовет.
Вызывайте Обновить() один раз в конце операции. Вызов внутри цикла после каждой записи заставит платформу гонять запрос столько раз, сколько итераций.
Обновление списка через ОповеститьОбИзменении и Оповестить
Когда объект записан из своей стандартной формы, открытые списки платформа обновляет сама. Проблема появляется, когда запись идёт кодом: из обработки, из серверной процедуры, из формы другого объекта.
Для этого есть ОповеститьОбИзменении. Ему можно передать ссылку или тип:
&НаКлиенте
Процедура ПослеЗаписиНаСервере(СозданныеДокументы)
// Вариант 1: по конкретной ссылке
Для Каждого Документ Из СозданныеДокументы Цикл
ОповеститьОбИзменении(Документ);
КонецЦикла;
// Вариант 2: сразу по типу, если записей много
ОповеститьОбИзменении(Тип("ДокументСсылка.РеализацияТоваровУслуг"));
КонецПроцедуры
Сам метод — клиентский. Сервер ничего «оповестить» не может, поэтому схема всегда одна: сервер возвращает ссылки на клиент, клиент вызывает оповещение.
Если открытой форме нужно не только перечитать список, но и, например, пересчитать итог в шапке, используйте пару Оповестить и ОбработкаОповещения:
// В обработке, создавшей документы
&НаКлиенте
Процедура СоздатьИОповестить(Команда)
СоздатьДокументыНаСервере();
Оповестить("ЗагрузкаРеализацийЗавершена", , ЭтотОбъект);
КонецПроцедуры
// В форме списка
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если ИмяСобытия = "ЗагрузкаРеализацийЗавершена" Тогда
Элементы.Список.Обновить();
ОбновитьИтогиНаСервере();
КонецЕсли;
КонецПроцедуры
Имя события — произвольная строка. Договоритесь о нём один раз и держите в одном месте, иначе опечатка в одной из форм молча сломает обновление.
Обновление динамического списка через параметры, отбор и запрос
Второй путь — изменить условия выборки. Любое изменение параметра, отбора или текста запроса платформа считает сигналом перечитать список. Отдельный Обновить() после этого не нужен.
Параметры запроса
Если в запросе списка есть &НачалоПериода и &КонецПериода, их значения задаются так:
&НаКлиенте
Процедура ПериодПриИзменении(Элемент)
Список.Параметры.УстановитьЗначениеПараметра("НачалоПериода", НачалоДня(Период.ДатаНачала));
Список.Параметры.УстановитьЗначениеПараметра("КонецПериода", КонецДня(Период.ДатаОкончания));
КонецПроцедуры
Начальные значения всех параметров задайте в ПриСозданииНаСервере. Незаполненный параметр приводит к ошибке при первом открытии формы.
Отбор
В конфигурациях на БСП (БП 3.0, ЗУП 3.1, УТ 11) отбор удобнее ставить общей процедурой. Она сама находит или создаёт элемент отбора:
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
Список, "Организация", Организация,
ВидСравненияКомпоновкиДанных.Равно, , ЗначениеЗаполнено(Организация));
Последний параметр — использование отбора. Когда реквизит пуст, отбор выключается, и список показывает все организации.
Текст запроса
Текст запроса меняют на сервере, у списка должен быть включён флаг ПроизвольныйЗапрос:
&НаСервере
Процедура ПереключитьРежимНаСервере()
Список.ТекстЗапроса = ПолучитьТекстЗапроса(РежимПросмотра);
КонецПроцедуры
Менять текст ради одного условия — дорогое решение: платформа заново разбирает запрос, а пользовательские настройки, завязанные на старые поля, могут слететь. Для условий хватает параметров и отбора.
Изменения из других сеансов, фоновых заданий и обмена РИБ
ОповеститьОбИзменении и Оповестить действуют только внутри одного клиентского сеанса. Если документ записал коллега на соседнем компьютере, регламентное задание или загрузка обмена в распределённой базе, ваш список об этом не узнает.
Вариантов три.
Автообновление элемента. У элемента таблицы есть свойства Автообновление и ПериодАвтообновления (в секундах). Их можно включить в конфигураторе или кодом:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Элементы.Список.Автообновление = Истина;
Элементы.Список.ПериодАвтообновления = 60;
КонецПроцедуры
Таймер со своим условием. Обработчик ожидания удобен, когда обновлять нужно не всегда, а, например, только пока идёт фоновое задание:
&НаКлиенте
Процедура ПроверитьФоновоеЗадание()
Если ЗаданиеВыполнено(ИдентификаторЗадания) Тогда
ОтключитьОбработчикОжидания("ПроверитьФоновоеЗадание");
Элементы.Список.Обновить();
КонецЕсли;
КонецПроцедуры
Функция ЗаданиеВыполнено здесь — ваша серверная функция, которая проверяет состояние задания. В БСП для этого есть готовые механизмы длительных операций с оповещением о завершении.
Явное обновление после обмена. Если обмен запускает сам пользователь из формы, вызовите ОповеститьОбИзменении по типам загруженных объектов в обработчике завершения.
Не ставьте период автообновления в несколько секунд на списках, которые открыты у десятков пользователей. Каждый тик — запрос к серверу от каждого клиента. Для большинства задач хватает 60–300 секунд.
Как обновлять динамический список без тормозов
Обновление — это повторный запрос. Если список открывается 5 секунд, то и каждое обновление займёт столько же. Ускорять нужно сам список:
- ⚡ Включите
ДинамическоеСчитываниеДанных. Тогда платформа читает данные порциями по мере прокрутки, а не весь результат сразу. - ⚡ Укажите основную таблицу. Без неё ключом строки будет не ссылка на объект, и позиционирование по ссылке, как в примере ниже, не сработает.
- ⚡ Не тяните виртуальные таблицы остатков без условий. Параметры виртуальной таблицы (период, отбор по организации) ставьте в её параметрах, а не в
ГДЕ. - ⚡ Сортируйте по индексированным полям. Сортировка по вычисляемому полю заставляет СУБД обрабатывать всю выборку.
- ⚡ Держите лёгким
ПриПолученииДанныхНаСервере. Этот обработчик вызывается на каждую порцию строк. Запрос внутри него на 50 строк — 50 лишних обращений к базе при каждом обновлении. - ⚡ Обновляйте один раз. Собирайте ссылки в массив, после операции вызывайте одно оповещение по типу.
При массовой загрузке сотен документов одно ОповеститьОбИзменении по типу работает быстрее, чем по каждой ссылке отдельно.
Обычные и управляемые формы: в чём разница
Код из управляемой формы в обычной не заработает, и наоборот.
| Действие | Управляемая форма | Обычная форма |
|---|---|---|
| Обновить список | Элементы.Список.Обновить() на клиенте | метод ОбновитьСтроки() табличного поля |
| Доступ к элементу | Элементы.ИмяЭлемента | ЭлементыФормы.ИмяЭлемента |
| Отбор | Список.Отбор (схема компоновки) | Отбор реквизита-списка с методами старого отбора |
| Контекст выполнения | клиент и сервер разделены | весь модуль формы выполняется в одном контексте |
| Оповещение других форм | ОповеститьОбИзменении, Оповестить | тот же механизм оповещений |
Главное отличие — разделение клиента и сервера. В обычной форме можно записать документ и тут же обновить список в одной процедуре. В управляемой запись идёт в серверной процедуре, обновление — в клиентской, после возврата из серверного вызова.
Если конфигурация работает в режиме управляемого приложения (БП 3.0, ЗУП 3.1, УТ 11), всё сказанное про обычные формы к ней не относится.
Пример: создаём документ на сервере и показываем его в списке
Задача: в форме списка реализаций есть кнопка «Создать по шаблону». Документ создаётся на сервере, после чего список должен обновиться, а курсор встать на новую строку.
&НаКлиенте
Процедура СоздатьПоШаблону(Команда)
НовыйДокумент = СоздатьПоШаблонуНаСервере();
Если Не ЗначениеЗаполнено(НовыйДокумент) Тогда
Возврат;
КонецЕсли;
ОповеститьОбИзменении(НовыйДокумент);
Элементы.Список.ТекущаяСтрока = НовыйДокумент;
КонецПроцедуры
&НаСервере
Функция СоздатьПоШаблонуНаСервере()
ТекущаяСтрока = Элементы.Список.ТекущаяСтрока;
Если ТекущаяСтрока = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
Документ = ТекущаяСтрока.Скопировать();
Документ.Дата = ТекущаяДатаСеанса();
Попытка
Документ.Записать(РежимЗаписиДокумента.Запись);
Исключение
ОбщегоНазначения.СообщитьПользователю(КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
Возврат Неопределено;
КонецПопытки;
Возврат Документ.Ссылка;
КонецФункции
Что здесь происходит:
- Клиент вызывает серверную функцию. Она копирует документ под курсором и записывает копию без проведения.
- Сервер возвращает ссылку или
Неопределено, если копировать нечего или запись не прошла. - Клиент оповещает об изменении. Список перечитывается, причём и в других открытых формах этого сеанса.
- Присвоение
ТекущаяСтрокаставит курсор на созданный документ. Для динамического списка текущая строка — это ключ, то есть ссылка.
Если на списке стоит отбор по периоду, а копия получила сегодняшнюю дату за его пределами, позиционироваться будет не на что: строка просто не попадёт в выборку. В таком случае перед присвоением ТекущаяСтрока расширьте период или снимите отбор.
Документ записывается без проведения, поэтому движений не появится, пока пользователь не проверит копию и не проведёт её сам. ОбщегоНазначения.СообщитьПользователю есть в конфигурациях на БСП; в самописной базе замените его на Сообщить.
Ошибки при программном обновлении и частые вопросы
Почему список не обновляется
- ❌ Запись кодом без оповещения. Обработка записала документы, но форма об этом не знает. Новые строки появляются только после переоткрытия. Лечится возвратом ссылок на клиент и вызовом
ОповеститьОбИзменении. - ❌ Обновление на сервере.
Элементы.Список.Обновить()в процедуре&НаСерверене компилируется. На сервере можно менять параметры и отбор: список перечитается, когда управление вернётся на клиент. - ❌ Даты без границ дня. Параметр
КонецПериода, равный дате без времени, отсекает все документы этого дня позже 00:00:00. ИспользуйтеКонецДня(). - ❌ Документ не проходит отбор. Код создал документ с датой вне выбранного периода или по другой организации. Список обновился честно, просто строка отфильтрована. Проверьте отборы и параметры формы, прежде чем искать ошибку в коде.
- ❌ Ожидание мгновенного обновления у коллег. Оповещения и
Обновить()работают только в своём сеансе. - ❌ Не тот тип в оповещении.
ОповеститьОбИзмененииждёт ссылку или тип ссылки:Тип("ДокументСсылка.РеализацияТоваровУслуг"). Тип объекта (ДокументОбъект…) или имя метаданных строкой списки не обновят. - ❌ Список с произвольным запросом без основной таблицы. Оповещение по типу не к чему привязать, и такой список остаётся прежним. Укажите основную таблицу или обновляйте его через
Обновить().