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