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