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