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