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