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