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