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