В практике разработки на платформе 1С:Предприятие 8.3 программисты нередко сталкиваются с ситуацией, которую в шутку называют «мистикой». Суть проблемы заключается в следующем: мы вызываем серверную функцию, она успешно отрабатывает, изменяет данные в базе и возвращает ссылку. Мы присваиваем эту ссылку реквизиту формы, но визуально в поле ввода отображается старое наименование или старое представление объекта. Кажется, что присваивание не сработало, хотя отладчик утверждает обратное.
В этой статье мы подробно разберем, почему так происходит, как работает механизм кэширования представлений и какие инструменты предоставляет платформа для решения этой задачи.
Рассмотрим типичный фрагмент кода, который вызывает подобные затруднения. В данном примере выполняется запись файла в справочник и обновление реквизита объекта на форме:
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
// ... подготовка данных ...
Объект.ФайлПротокола = ЗаписатьФайлВСправочник(АдресФайла, ПараметрыФайла);
Модифицированность = Истина;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ЗаписатьФайлВСправочник(Знач АдресФайла, Знач ПараметрыФайла)
Если Не ЗначениеЗаполнено(ПараметрыФайла.ВладелецФайлов.ФайлПротокола) Тогда
Результат = РаботаСФайлами.ДобавитьФайл(ПараметрыФайла, АдресФайла);
Иначе
// Обновляем существующий файл
РаботаСФайлами.ОбновитьФайл(ПараметрыФайла.ВладелецФайлов.ФайлПротокола, ПараметрыФайла);
Результат = ПараметрыФайла.ВладелецФайлов.ФайлПротокола;
КонецЕсли;
Возврат Результат;
КонецФункции
Проанализируем происходящее: функция ЗаписатьФайлВСправочник возвращает ссылку на файл. Если файл создается впервые, проблем обычно нет. Но если файл обновляется (блок Иначе), ссылка остается прежней (тот же GUID), хотя данные объекта в базе (например, наименование или версия) изменились. На клиенте мы видим старое название файла до тех пор, пока не выполним какое-то специфическое действие, заставляющее форму перечитать данные.
Чтобы понять природу этой «мистики», нам нужно заглянуть «под капот» платформы 1С. Когда клиентское приложение отображает ссылку в поле формы, оно не запрашивает данные с сервера каждый раз при перерисовке окна. Платформа использует локальный кэш представлений ссылок.
Алгоритм работы клиента выглядит так:
В нашем случае ссылка на ФайлПротокола не изменилась. Для платформы это тот же самый GUID. Следовательно, клиент считает, что запрашивать новое строковое представление не нужно, и продолжает показывать старое название, которое хранится в кэше с момента открытия формы.
Использование директивы &НаСервереБезКонтекста повышает производительность, так как на сервер не передаются данные всей формы. Однако у этого есть обратная сторона: платформа не выполняет автоматическую синхронизацию состояния всех реквизитов формы после завершения вызова. Мы получаем лишь возвращаемое значение функции. Даже если мы присваиваем это значение реквизиту Объект.ФайлПротокола, платформа видит, что значение переменной (сама ссылка) фактически не изменилось, и не инициирует принудительное обновление визуального ряда для этого элемента.
Самым эффективным и правильным способом заставить систему «забыть» старое представление ссылки является использование глобального метода ОповеститьОбИзменении(). Рассмотрим, как это применить в коде:
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
СсылкаНаФайл = ЗаписатьФайлВСправочник(АдресФайла, ПараметрыФайла);
Объект.ФайлПротокола = СсылкаНаФайл;
// Принудительно уведомляем систему, что объект за этой ссылкой изменился
ОповеститьОбИзменении(СсылкаНаФайл);
Модифицированность = Истина;
КонецПроцедуры
Метод ОповеститьОбИзменении выполняет несколько важных действий:
Иногда требуется обновить не только кэш ссылок, но и все элементы управления текущей формы, которые могли быть изменены программно. Для этого используется метод формы ОбновитьОтображениеДанных().
Разберем разницу: если ОповеститьОбИзменении работает глобально в рамках всего сеанса пользователя, то ЭтаФорма.ОбновитьОтображениеДанных() воздействует только на текущую форму. Рассмотрим пример его вызова:
&НаКлиенте
Процедура ПослеОбновленияДанных()
// Принудительная перерисовка элементов формы
ЭтаФорма.ОбновитьОтображениеДанных();
КонецПроцедуры
Этот метод полезен, когда вы программно меняете связанные реквизиты, и хотите, чтобы платформа пересчитала видимость, доступность или текстовые значения полей ввода без переоткрытия формы.
Автор исходного вопроса заметил интересную деталь: после нажатия кнопки открытия файла представление в форме становилось правильным. Это объясняется тем, что при открытии объекта или выполнении действий с ним (например, получение двоичных данных через серверный вызов) платформа часто инициирует неявное обновление состояния объекта. Любой контекстный вызов сервера (&НаСервере) с последующим возвратом управления на клиент выполняет частичную синхронизацию, что может привести к обновлению кэша представлений, но полагаться на этот эффект не стоит — лучше использовать явные методы оповещения.
Проанализировав ситуацию, мы можем сформулировать следующие правила для качественной разработки управляемого интерфейса:
ОповеститьОбИзменении нужен не только для динамических списков, но и для корректной работы полей ввода со ссылками.&НаСервере (контекстной), платформа делает больше работы за вас. Но при использовании &НаСервереБезКонтекста ответственность за актуализацию данных на клиенте полностью ложится на плечи разработчика.DataVersion) меняется. Клиент 1С не отслеживает изменение версии объекта автоматически для каждой ссылки в реальном времени ради экономии трафика.Таким образом, «мистика» с не обновляющимися значениями — это всего лишь следствие оптимизации платформы. Использование ОповеститьОбИзменении(Ссылка) — это стандартный, штатный способ синхронизации состояния базы данных и визуального интерфейса пользователя.