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

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

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

Выясним причину: почему данные «теряются»?

Для начала разберем архитектурную особенность управляемого приложения. Когда мы открываем форму объекта (например, документа), система создает в памяти сервера и передает на клиент специальную структуру — ДанныеФормыСтруктура. Это не сам объект из базы данных, а его «проекция» для конкретного окна.

Если мы открываем вторую (дополнительную) форму для того же самого объекта, платформа создает вторую независимую проекцию. Важно понимать: Объект в основной форме и Объект в дополнительной форме — это два разных набора данных в памяти. Изменения, внесенные в Объект внутри контекста доп. формы, существуют только до тех пор, пока эта форма открыта. Как только форма закрывается, её контекст (и все измененные в нем данные) уничтожается, если они не были явно переданы или записаны в базу данных.

Рассмотрим ситуацию: разные формы — разные объекты. Даже если они отображают одну и ту же ссылку на документ, их внутренние коллекции данных в памяти разделены. Поэтому простое присвоение значения реквизиту Объект.МойРеквизит = "Значение" в доп. форме никак не влияет на Объект основной формы.

Способ 1: Прямое присвоение через Владельца формы

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

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


&НаКлиенте
Процедура ЗавершитьЗаполнение(Команда)
    // Проверяем, что владелец формы задан и у него есть Объект
    Если ВладелецФормы <> Неопределено Тогда
        ВладелецФормы.Объект.МойРеквизит = Объект.МойРеквизит;
    КонецЕсли;
    Закрыть();
КонецПроцедуры

Важные нюансы этого метода:

  1. Отсутствие типизации: Конфигуратор не знает заранее, кто будет владельцем формы, поэтому подсказки (контекстное меню) при вводе ВладелецФормы.Объект... работать не будут.
  2. Только на клиенте: Обращение к владельцу формы доступно только в клиентском контексте.
  3. Риск ошибки: Если форму откроют программно без указания владельца, попытка обращения к свойству Объект вызовет исключение.

Способ 2: Использование механизма ОписанияОповещения (Callback)

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

Разберем этот метод по шагам.

Шаг 1. В основной форме прописываем открытие вспомогательной формы с указанием обработчика оповещения:


&НаКлиенте
Процедура ОткрытьДопФорму(Команда)
    // Указываем процедуру "ОбработатьРезультат", которая выполнится после закрытия
    Оповещение = Новый ОписаниеОповещения("ОбработатьРезультат", ЭтотОбъект);
    
    ПараметрыФормы = Новый Структура;
    // Передаем текущие данные, если нужно
    
    ОткрытьФорму("Документ.МойДокумент.Форма.ДополнительнаяФорма", ПараметрыФормы, ЭтотОбъект,,,, Оповещение);
КонецПроцедуры

&НаКлиенте
Процедура ОбработатьРезультат(Результат, ДополнительныеПараметры) Экспорт
    Если Результат <> Неопределено Тогда
        // Заполняем реквизит основного объекта данными из результата
        Объект.МойРеквизит = Результат.ЗначениеРеквизита;
    КонецЕсли;
КонецПроцедуры

Шаг 2. В дополнительной форме при закрытии передаем данные обратно:


&НаКлиенте
Процедура ЗаписатьИЗакрыть(Команда)
    СтруктураРезультата = Новый Структура;
    СтруктураРезультата.Вставить("ЗначениеРеквизита", Объект.МойРеквизит);
    
    // Передаем структуру в метод Закрыть
    Закрыть(СтруктураРезультата);
КонецПроцедуры

Этот метод считается более «чистым», так как вспомогательная форма ничего не знает о внутреннем устройстве основной формы. Она просто возвращает результат, а основная форма сама решает, куда его пристроить.

Способ 3: Глобальные оповещения

Рассмотрим ситуацию, когда формы не связаны напрямую отношениями «владелец-подчиненный». В этом случае можно использовать механизм Оповестить().

В дополнительной форме вызываем метод:

Оповестить("ОбновлениеРеквизита", Объект.МойРеквизит, ЭтаФорма);

В основной форме (и во всех других открытых формах) сработает обработчик ОбработкаОповещения. Посмотрим на пример реализации:


&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
    Если ИмяСобытия = "ОбновлениеРеквизита" Тогда
        Объект.МойРеквизит = Параметр;
    КонецЕсли;
КонецПроцедуры

Специфика работы в расширениях

Поскольку проблема возникла при работе с расширением, важно учитывать еще один момент. Проанализируем настройки элементов формы. Если вы добавили реквизит в расширении и вывели его на форму, убедитесь, что свойство ПутьКДанным у элемента формы (поля ввода) жестко связано с Объект.МойРеквизит.

Если данные не сохраняются даже после записи документа, проверьте следующее:

Подводя итог, можно сказать, что работа с несколькими формами одного объекта требует дисциплины в передаче данных. Использование ОписаниеОповещения является «золотым стандартом», который убережет вас от ошибок при изменении структуры конфигурации в будущем.

← На главную