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