Как сделать таблицу значений на форме пополняемой и правильно выводить ее в табличный документ

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке интерфейсов в среде «1С:Предприятие 8» (особенно на управляемых формах) часто возникает задача организации пошагового или итерационного ввода данных. Типичный сценарий: пользователь открывает вспомогательное диалоговое окно (Форму 2), вводит информацию об одном объекте (например, о сотруднике, товаре или услуге), закрывает форму, а введенная строка добавляется в общую таблицу на основной форме (Форме 1). Результат при этом должен визуализироваться в поле табличного документа.

Начинающие разработчики часто сталкиваются с тем, что при повторном вызове вспомогательной формы старые данные бесследно исчезают, таблица заполняется заново либо на форме дублируются области вывода. В этой статье мы подробно разберем причины такого поведения и построим правильную архитектуру взаимодействия форм, накопления данных и их визуализации.

В чем заключается главная ошибка?

Проанализируем ситуацию, описанную в теме форума. Разработчик пытается организовать накопление данных, возвращаемых из второй формы. Однако при каждой итерации таблица создается заново или очищается. Ошибка кроется в непонимании разницы между двумя важнейшими механизмами платформы:

  1. Хранилище данных (Реквизит формы) — это коллекция, в которой физически находятся ваши данные в оперативной памяти на протяжении всей работы формы.
  2. Средство отображения (Табличный документ) — это графический «холст», предназначенный исключительно для форматированного вывода данных пользователю.

Если вы при каждом возврате данных заново инициализируете таблицу значений через конструктор Новый ТаблицаЗначений, старый объект уничтожается сборщиком мусора, а вместе с ним теряются и все ранее накопленные строки. Если же вы используете правильный реквизит формы, но при этом очищаете его перед выводом (путаете с очисткой поля табличного документа), результат будет аналогичным — в таблице всегда будет оставаться только последняя введенная строка.


Разница между локальной ТЗ и реквизитом формы типа «Таблица значений»

Для успешного решения задачи нам необходимо четко разграничивать типы данных, с которыми мы работаем на клиенте и на сервере.

Обычный объект ТаблицаЗначений не может существовать на клиенте в управляемом приложении. Он доступен только на стороне сервера. Передать его «напрямую» с сервера на клиент невозможно, так как этот тип не является мутабельным (передаваемым между контекстами).

Когда вы создаете реквизит управляемой формы и задаете ему тип ТаблицаЗначений, платформа 1С выполняет автоматическую проекцию. На клиенте этот реквизит превращается в объект типа ДанныеФормыКоллекция, а на сервере он по-прежнему доступен для полноценных манипуляций. Главное свойство реквизита формы — он живет все время, пока открыта сама форма. Данные из него никуда не исчезают между серверными вызовами и клиентскими событиями.

Поэтому правильное правило звучит так: никогда не создавайте динамическую таблицу значений через оператор Новый, если вам нужно накапливать данные в процессе работы формы. Используйте заранее созданный реквизит формы!


Пошаговый алгоритм реализации пополняемой таблицы

Разберем по шагам, как правильно настроить логику накопления данных и их вывода в табличный документ без потери предыдущих итераций.

Шаг 1. Создание реквизитов формы

В основной форме (Форма 1) на вкладке «Реквизиты» добавим новый реквизит. Назовем его, например, Сотрудники и установим тип данных ТаблицаЗначений. Внутри этого реквизита создадим необходимые колонки (например, ФИО с типом Строка, Должность с типом Строка и Оклад с типом Число).

Также нам понадобится реквизит формы для вывода отчета — назовем его ТабличныйДокумент с типом ТабличныйДокумент. Перетащим его на рабочую область формы, чтобы пользователь видел результат визуально.

Шаг 2. Организация асинхронного вызова второй формы

В управляемом приложении открытие подчиненных окон выполняется асинхронно. Для передачи данных обратно в первую форму используется механизм описания оповещения. Напишем обработчик команды добавления строки на клиенте в Форме 1:


&НаКлиенте
Процедура ДобавитьСтроку(Команда)
    
    // Создаем описание оповещения, которое укажет, какую процедуру вызвать после закрытия Формы 2
    Оповещение = Новый ОписаниеОповещения("ДобавитьСтрокуЗавершение", ЭтотОбъект);
    
    // Открываем форму ввода данных (Форма 2)
    ОткрытьФорму("ОбщаяФорма.ФормаВводаДанных", , ЭтотОбъект, , , , Оповещение);
    
КонецПроцедуры

Шаг 3. Возврат данных из второй формы

Во второй форме пользователь заполняет реквизиты. При нажатии кнопки сохранения мы должны упаковать эти данные в структуру и передать их в метод закрытия формы:


&НаКлиенте
Процедура ЗаписатьИЗакрыть(Команда)
    
    // Собираем введенные пользователем данные в структуру
    СтруктураДанных = Новый Структура("ФИО, Должность, Оклад");
    ЗаполнитьЗначенияСвойств(СтруктураДанных, ЭтотОбъект);
    
    // Закрываем форму и передаем структуру в качестве результата
    Закрыть(СтруктураДанных);
    
КонецПроцедуры

Шаг 4. Прием данных и добавление строки в реквизит-накопитель

Когда Форма 2 закрывается, управление возвращается в процедуру-колбэк первой формы. Именно здесь совершается магия накопления. Обратите внимание: мы не очищаем коллекцию Сотрудники, а только добавляем в нее новые элементы.


&НаКлиенте
Процедура ДобавитьСтрокуЗавершение(Результат, ДополнительныеПараметры) Экспорт
    
    // Если пользователь закрыл форму крестиком или отменил ввод
    Если Результат = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Добавляем строку в существующий реквизит формы (накапливаем данные!)
    НоваяСтрока = Сотрудники.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрока, Результат);
    
    // Теперь, когда данные сохранены в ТЗ, обновляем визуальное отображение на сервере
    ОбновитьТабличныйДокументНаСервере();
    
КонецПроцедуры

Шаг 5. Правильное обновление и вывод Табличного документа

А теперь разберем ключевой момент, из-за которого возникала путаница у автора темы. Чтобы вывести обновленный список сотрудников, мы должны полностью перерисовать наш табличный документ. Если мы не очистим табличный документ, новые данные будут дописываться снизу к старым, создавая хаос на экране.

Поэтому мы очищаем только визуальное поле (табличный документ), но саму таблицу-источник (реквизит Сотрудники) оставляем нетронутой. Проанализируем серверную процедуру вывода:


&НаСервере
Процедура ОбновитьТабличныйДокументНаСервере()
    
    // Очищаем ТОЛЬКО табличный документ (холст), чтобы старые строки не дублировались
    ТабличныйДокумент.Очистить();
    
    // Получаем макет нашей обработки/отчета
    Макет = ПолучитьМакет("Макет");
    
    // Инициализируем области макета
    ОбластьШапка  = Макет.ПолучитьОбласть("Шапка");
    ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
    
    // Выводим шапку таблицы в чистый табличный документ
    ТабличныйДокумент.Вывести(ОбластьШапка);
    
    // Обходим циклом все накопленные в реквизите формы данные
    Для Каждого Стр Из Сотрудники Цикл
        
        // Заполняем параметры области строки данными текущего сотрудника
        ОбластьСтрока.Параметры.Заполнить(Стр);
        
        // Выводим строку в табличный документ
        ТабличныйДокумент.Вывести(ОбластьСтрока);
        
    КонецЦикла;
    
КонецПроцедуры

Альтернативный способ быстрого вывода без макета

Рассмотрим ситуацию, когда детальная верстка и сложный дизайн табличного документа не требуются (например, на этапе отладки или для простых внутренних отчетов). В платформе «1С:Предприятие 8» существует отличный альтернативный способ быстро вывести плоскую таблицу значений на экран без ручного обхода строк в цикле и создания макетов. Для этого мы можем применить объект ПостроительОтчета.

Посмотрим на пример реализации такого вывода на сервере:


&НаСервере
Процедура ВывестиТЗБыстроЧерезПостроитель()
    
    // Традиционно очищаем холст отображения
    ТабличныйДокумент.Очистить();
    
    // Преобразуем реквизит формы "Сотрудники" в реальный объект ТаблицаЗначений
    ТаблицаДляВывода = РеквизитФормыВЗначение("Сотрудники");
    
    // Используем ПостроительОтчета для генерации печатной формы
    Построитель = Новый ПостроительОтчета;
    Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТаблицаДляВывода);
    Построитель.Выполнить();
    
    // Выводим сгенерированный результат напрямую в наш табличный документ
    Построитель.Вывести(ТабличныйДокумент);
    
КонецПроцедуры

Этот метод чрезвычайно удобен тем, что платформа автоматически построит шапку с названиями колонок, настроит ширину столбцов и выведет все строки вашей таблицы значений буквально в несколько строк кода. Главное помнить, что метод РеквизитФормыВЗначение временно создает полноценную копию таблицы значений на сервере для передачи ее в источник данных построителя.


Резюме ключевых правил для разработчика

Подводя итоги нашего разбора, сформулируем краткие и важные правила, которые уберегут вас от подобных архитектурных ошибок в будущем:

Понимание клиент-серверного взаимодействия управляемых форм и разграничения объектов отображения от объектов хранения данных позволит вам создавать быстрые, надежные и интуитивно понятные интерфейсы в любых прикладных решениях 1С.

← На главную