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