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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В разработке на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда данные в базе данных уже изменены программно, но пользователь в открытой форме списка продолжает видеть старые значения. Эта проблема особенно актуальна для управляемых форм, где взаимодействие между клиентом и сервером строго регламентировано. В этой статье мы подробно разберем, почему динамический список «молчит» и какие механизмы платформы заставляют его отобразить актуальную информацию.

Анализ проблемы: почему автоматика не всегда срабатывает

Рассмотрим стандартную ситуацию: в форме списка есть кнопка, которая открывает диалог ввода значения, после чего выполняется серверная процедура для записи этого значения в объект. Проблема заключается в том, что когда мы вызываем метод Записать() внутри серверной процедуры (особенно &НаСервереБезКонтекста), контекст текущей формы не «знает», что данные в базе данных изменились. Динамический список оптимизирован для работы с большими объемами данных и не перечитывает базу данных каждую секунду без веской причины.

Проанализируем типичный фрагмент кода, который приводит к подобному вопросу:


&НаКлиенте
Процедура ВводНомераЗавершение(Рез, Доп) Экспорт
    Если Рез <> Неопределено Тогда
        УстановитьНомерТ60(Элементы.Список.ТекущаяСтрока, Рез);    
        // Здесь список должен обновиться, но этого не происходит
    КонецЕсли; 
КонецПроцедуры

&НаСервереБезКонтекста
Процедура УстановитьНомерТ60(Ссыль, Номер)
    Об = Ссыль.ПолучитьОбъект();
    Об.ОбменДанными.Загрузка = Истина;
    Об.НумОт_НомерТ60 = Номер;
    Об.Записать();
КонецПроцедуры 

В данном примере объект модифицируется «тихо». Платформа не связывает изменение конкретной записи в таблице базы данных с необходимостью немедленного обновления конкретного элемента формы на клиенте у данного пользователя.

Решение №1: Глобальное оповещение системы

Самым правильным и эффективным способом решения задачи является использование метода ОповеститьОбИзменении(). Рассмотрим, как он работает. Этот метод сообщает всем открытым формам в текущем сеансе, что данные определенного объекта (ссылки) изменились.

Разберем, как изменить код процедуры завершения:


&НаКлиенте
Процедура ВводНомераЗавершение(Рез, Доп) Экспорт
    Если Рез <> Неопределено Тогда
        // Сохраняем ссылку, которую меняли
        ТекСсылка = Элементы.Список.ТекущаяСтрока;
        УстановитьНомерТ60(ТекСсылка, Рез);    
        
        // Оповещаем систему об изменении объекта
        ОповеститьОбИзменении(ТекСсылка);
    КонецЕсли; 
КонецПроцедуры

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

Решение №2: Принудительное обновление элемента формы

Иногда требуется полностью обновить содержимое списка, например, если изменение одного объекта могло повлиять на отображение других строк (через сложные связи в запросе). Для этого существует метод элемента формы Обновить().

Посмотрим на синтаксис:

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

Важные нюансы метода Обновить():

  1. Он заставляет динамический список полностью перезапустить запрос к базе данных.
  2. Это более «тяжелая» операция по сравнению с оповещением об изменении.
  3. Если список построен на произвольном запросе без указания основной таблицы, метод Обновить() — это единственный способ увидеть изменения без перезакрытия формы.

Роль «Основной таблицы» в динамическом списке

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

Если это свойство не заполнено:

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

Оптимизация работы на сервере: избегаем лишних действий

Проанализируем серверную часть кода. Часто разработчики используют ПолучитьОбъект() только для того, чтобы изменить один реквизит. Посмотрим, почему это может быть избыточно. Метод ПолучитьОбъект() считывает из базы данных всё: все реквизиты, все табличные части, все движения. Если объект «тяжелый» (например, огромная накладная), это создает лишнюю нагрузку.

Если вам нужно просто проверить существование объекта перед записью, рассмотрите использование легкого запроса вместо получения объекта. Однако, если запись неизбежна, обратите внимание на использование флага ОбменДанными.Загрузка = Истина.

Зачем нужен ОбменДанными.Загрузка?

В коде автора это используется для обхода проверок в модуле объекта. Это ускоряет запись, но помните: при таком подходе не выполняются бизнес-проверки, и данные могут стать некорректными с точки зрения логики конфигурации. Если вы используете этот флаг, вы берете на себя полную ответственность за целостность данных.

Безопасное получение текущих данных строки

Рассмотрим еще один важный технический момент. В коде часто встречается обращение к Элементы.Список.ТекущаяСтрока. Важно помнить, что в этом свойстве хранится только ссылка (или идентификатор строки). Если вам нужны значения других полей, которые уже отображены в списке (например, номер или дата), лучше использовать ТекущиеДанные.

Сравним подходы:


// Подход 1: Только ссылка
ТекСсылка = Элементы.Список.ТекущаяСтрока;
Если ТекСсылка <> Неопределено Тогда
    // Работаем со ссылкой
КонецЕсли;

// Подход 2: Все данные строки (без обращения к серверу)
ДанныеСтроки = Элементы.Список.ТекущиеДанные;
Если ДанныеСтроки <> Неопределено Тогда
    СтарыйНомер = ДанныеСтроки.НумОт_НомерТ60; // Доступ к полям списка
КонецЕсли;

Использование ТекущиеДанные позволяет избежать лишних серверных вызовов, если вам нужно просто прочитать значение, которое пользователь уже видит на экране.

Типизация для удобства разработки (IntelliSense)

В ходе обсуждения на форуме был упомянут интересный прием для «обмана» конфигуратора, чтобы работала подсказка кода (IntelliSense) в серверных процедурах без контекста. Поскольку 1С — язык с динамической типизацией, конфигуратор не всегда понимает, что за объект находится в переменной.

Рассмотрим классический «хак»:


&НаСервереБезКонтекста
Процедура УстановитьНомерТ60(Ссыль, Номер)
    Об = Ссыль.ПолучитьОбъект();
    
    // Этот блок никогда не выполнится, но подсказка (точка) заработает
    Если Ложь Тогда    
        Об = Документы.Отпуск.СоздатьДокумент();
    КонецЕсли; 
    
    Об.НумОт_НомерТ60 = Номер; // Теперь здесь работает автодополнение
    Об.Записать();
КонецПроцедуры 

Хотя этот метод до сих пор популярен, современные инструменты (например, EDT) или простое знание структуры метаданных позволяют обходиться без подобных конструкций. Тем не менее, для быстрой разработки в старом конфигураторе это остается рабочим приемом.

Итоги и рекомендации

Подведем итог нашему анализу ситуации с необновляющимся списком:

  1. Всегда используйте ОповеститьОбИзменении(Ссылка) после программной записи объекта на сервере, если хотите, чтобы изменения отобразились в открытых формах списков.
  2. Проверяйте, установлена ли Основная таблица в свойствах динамического списка. Без неё автоматическое отслеживание изменений практически не работает.
  3. Если список сложный (произвольный запрос с объединениями и соединениями), используйте Элементы.Список.Обновить() для полной перезагрузки данных.
  4. Для проверки наличия строки в списке на клиенте используйте Элементы.Список.ТекущиеДанные — это надежнее и информативнее, чем проверка просто ТекущейСтроки.
  5. При программной записи объекта на сервере старайтесь минимизировать количество операций. Если возможно, используйте ОбменДанными.Загрузка = Истина для ускорения, но делайте это осознанно.

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

← На главную