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

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

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

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

Основные принципы работы с динамическими элементами и реквизитами формы

Прежде чем мы перейдем к коду, важно понять ключевые понятия платформы 1С:Предприятие:

  1. Реквизит формы: Это данные, которые хранятся в контексте формы. Они могут быть простыми типами (строка, число, булево) или сложными (ссылки на объекты, ТаблицаЗначений, ДеревоЗначений). Реквизиты формы создаются и изменяются методом ИзменитьРеквизиты(), который доступен только на сервере.
  2. Элемент формы: Это визуальный компонент интерфейса (поле ввода, кнопка, таблица), который отображается на форме. Элементы формы создаются и удаляются методами коллекции Элементы формы (например, Элементы.Добавить(), Элементы.Удалить()), и могут быть связаны с реквизитами формы через свойство ПутьКДанным.

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

Шаг 1: Создание реквизита формы типа ТаблицаЗначений и его колонок

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

Представим, что наша таблица будет называться "ИмяРеквизитаТЗ", а одна из ее колонок – "Организация".


// Пример на сервере
&НаСервере
Процедура СоздатьДинамическуюТаблицуНаФорме()
    МассивДобавляемыхРеквизитов = Новый Массив;

    // 1. Создаем сам реквизит формы типа ТаблицаЗначений
    МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("ИмяРеквизитаТЗ", Новый ОписаниеТипов("ТаблицаЗначений")));

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

    // Вызываем ИзменитьРеквизиты для добавления реквизитов в форму.
    // МассивУдаляемыхРеквизитов пока пустой, мы его используем позже для удаления.
    ИзменитьРеквизиты(МассивДобавляемыхРеквизитов, Новый Массив); 
КонецПроцедуры

В этом коде:

  1. Мы создаем массив МассивДобавляемыхРеквизитов, который будет содержать описания всех реквизитов, которые мы хотим добавить в форму.
  2. Первый вызов Новый РеквизитФормы("ИмяРеквизитаТЗ", Новый ОписаниеТипов("ТаблицаЗначений")) создает сам реквизит формы, который будет хранить данные. Обратите внимание, что это реквизит *формы*, а не колонка.
  3. Последующие вызовы Новый РеквизитФормы(...) с тремя и четырьмя параметрами создают описания колонок для нашей ТаблицыЗначений. Третий параметр ("ИмяРеквизитаТЗ") указывает на родительский реквизит (нашу таблицу значений), а четвертый параметр (например, "Организация") — это имя самой колонки в этой таблице.
  4. Метод ОписаниеТипов() позволяет нам указать тип данных для каждой колонки. Например, "СправочникСсылка.Организации" для колонки с ссылкой на справочник "Организации", "Строка" для строковых данных, "Число" для числовых и так далее.

Шаг 2: Создание элементов формы ТаблицаФормы и его колонок

После того как мы создали реквизиты формы на сервере, нам нужно создать соответствующие им визуальные элементы на самой форме. Это делается с помощью коллекции Элементы формы.

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


// Пример на клиенте после вызова СоздатьДинамическуюТаблицуНаФорме() на сервере
&НаКлиенте
Процедура ВывестиДинамическуюТаблицуНаФорму()
    // Убедимся, что реквизиты формы уже созданы на сервере
    СоздатьДинамическуюТаблицуНаФорме(); // Вызов серверной процедуры

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

    // 2. Создаем элемент ТаблицаФормы
    ТаблицаФормы = Элементы.Добавить("ИмяЭлементаТЗ", Тип("ТаблицаФормы"), ГруппаДляТЗ);
    ТаблицаФормы.Заголовок = "Тут заголовок таблицы";
    ТаблицаФормы.ПутьКДанным = "ИмяРеквизитаТЗ"; // Связываем элемент с реквизитом формы
    ТаблицаФормы.РастягиватьПоГоризонтали = Истина;
    ТаблицаФормы.РастягиватьПоВертикали = Истина;
    ТаблицаФормы.ИзменятьСоставСтрок = Истина; // Разрешить добавление/удаление строк

    // 3. Создаем элементы ПолеФормы для каждой колонки нашей таблицы
    // ИмяРеквизитаТЗ - это имя реквизита формы типа ТаблицаЗначений
    // Организация - это имя колонки в нашей ТаблицеЗначений
    
    // Колонка "Организация"
    НоваяКолонкаОрганизация = Элементы.Добавить("ИмяЭлементаТЗОрганизация", Тип("ПолеФормы"), ТаблицаФормы);
    НоваяКолонкаОрганизация.Заголовок = "Организация";
    НоваяКолонкаОрганизация.ПутьКДанным = "ИмяРеквизитаТЗ.Организация"; // Путь к данным в таблице
    НоваяКолонкаОрганизация.Вид = ВидПоляФормы.ПолеВвода; // Или ВидПоляФормы.ПолеНадписи для только просмотра

    // Колонка "Показатель"
    НоваяКолонкаПоказатель = Элементы.Добавить("ИмяЭлементаТЗПоказатель", Тип("ПолеФормы"), ТаблицаФормы);
    НоваяКолонкаПоказатель.Заголовок = "Показатель";
    НоваяКолонкаПоказатель.ПутьКДанным = "ИмяРеквизитаТЗ.Показатель";
    НоваяКолонкаПоказатель.Вид = ВидПоляФормы.ПолеВвода;

    // Колонка "Значение"
    НоваяКолонкаЗначение = Элементы.Добавить("ИмяЭлементаТЗЗначение", Тип("ПолеФормы"), ТаблицаФормы);
    НоваяКолонкаЗначение.Заголовок = "Значение";
    НоваяКолонкаЗначение.ПутьКДанным = "ИмяРеквизитаТЗ.Значение";
    НоваяКолонкаЗначение.Вид = ВидПоляФормы.ПолеВвода;

    // После создания элементов, можно заполнить таблицу данными
    ЗаполнитьДанныеДинамическойТаблицы();
КонецПроцедуры

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

    // Очистим таблицу перед заполнением
    ЭтаФорма.ИмяРеквизитаТЗ.Очистить();

    // Добавляем тестовые данные
    НоваяСтрока = ЭтаФорма.ИмяРеквизитаТЗ.Добавить();
    НоваяСтрока.Организация = Справочники.Организации.НайтиПоКоду("000000001"); // Пример
    НоваяСтрока.Показатель = "Выручка";
    НоваяСтрока.Значение = 10000;

    НоваяСтрока = ЭтаФорма.ИмяРеквизитаТЗ.Добавить();
    НоваяСтрока.Организация = Справочники.Организации.НайтиПоКоду("000000002"); // Пример
    НоваяСтрока.Показатель = "Прибыль";
    НоваяСтрока.Значение = 2500;
КонецПроцедуры

В этом блоке кода:

  1. Мы используем Элементы.Добавить("ИмяЭлемента", Тип("ТипЭлемента"), Родитель) для создания элементов. Третий параметр Родитель очень важен, так как он определяет, где будет располагаться наш элемент. Если мы хотим, чтобы таблица была на определенной странице или в определенной группе, мы указываем эту страницу/группу в качестве родителя.
  2. Для элемента ТаблицаФормы (ИмяЭлементаТЗ) мы указываем свойство ПутьКДанным = "ИмяРеквизитаТЗ". Это связывает визуальный элемент с созданным нами реквизитом формы типа ТаблицаЗначений.
  3. Для каждой колонки мы создаем отдельный элемент ПолеФормы (например, ИмяЭлементаТЗОрганизация). Его родителем является созданный элемент ТаблицаФормы.
  4. Свойство ПутьКДанным для колонок указывается как "ИмяРеквизитаТЗ.ИмяКолонки" (например, "ИмяРеквизитаТЗ.Организация"). Это устанавливает связь между элементом ПолеФормы и конкретной колонкой в нашей таблице значений.
  5. ВидПоляФормы.ПолеВвода позволяет редактировать данные в колонке, а ВидПоляФормы.ПолеНадписи делает ее только для чтения.
  6. Процедура ЗаполнитьДанныеДинамическойТаблицы() демонстрирует, как мы можем обращаться к программно созданному реквизиту формы (ЭтаФорма.ИмяРеквизитаТЗ) и заполнять его данными, используя стандартные методы работы с ТаблицейЗначений. Важно при первом обращении убедиться, что реквизит не Неопределено, и, при необходимости, программно добавить колонки в саму ТаблицуЗначений, чтобы избежать ошибок при заполнении.

Шаг 3: Комплексный пример создания и размещения таблицы

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


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

    // Добавляем основной реквизит формы типа ТаблицаЗначений
    МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы(ИмяРеквизитаТаблицы, Новый ОписаниеТипов("ТаблицаЗначений")));

    // Добавляем реквизиты для каждой колонки
    Для Каждого ИмяКолонки Из СтрРазделить(СписокКолонокЧерезЗапятую, ",", Ложь) Цикл
        ИмяКолонки = СтрЗаменить(ИмяКолонки, " ", ""); // Удаляем пробелы
        Если ПустаяСтрока(ИмяКолонки) Тогда Продолжить КонецЕсли;

        // Здесь определяем тип колонки. В реальной задаче может быть сложнее
        ОписаниеТипаКолонки = Новый ОписаниеТипов("Строка"); 
        Если ИмяКолонки = "Организация" Тогда
            ОписаниеТипаКолонки = Новый ОписаниеТипов("СправочникСсылка.Организации");
        ИначеЕсли ИмяКолонки = "Значение" Тогда
            ОписаниеТипаКолонки = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2));
        КонецЕсли;

        МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы(ИмяКолонки, ОписаниеТипаКолонки, ИмяРеквизитаТаблицы, ИмяКолонки));
    КонецЦикла;

    // Применяем изменения к реквизитам формы
    ИзменитьРеквизиты(МассивДобавляемыхРеквизитов, МассивУдаляемыхРеквизитов);
КонецПроцедуры

&НаКлиенте
Процедура ВывестиДинамическуюТаблицуНаФормуПоСписку(ИмяРеквизитаТаблицы, СписокКолонокЧерезЗапятую, ЭлементРодитель)

    // Удаляем предыдущие элементы, если они были
    // Это очень важно при повторном вызове, чтобы избежать ошибки "Не уникальное имя элемента"
    Если Элементы.Найти("ГруппаДляДинамическойТЗ") <> Неопределено Тогда
        Элементы.Удалить(Элементы.ГруппаДляДинамическойТЗ);
    КонецЕсли;

    // 1. Создаем группу для размещения нашей таблицы
    ГруппаДляТЗ = Элементы.Добавить("ГруппаДляДинамическойТЗ", Тип("ГруппаФормы"), ЭлементРодитель);
    ГруппаДляТЗ.Вид = ВидГруппыФормы.ОбычнаяГруппа;
    ГруппаДляТЗ.Заголовок = "Динамические Данные";
    ГруппаДляТЗ.ОтображатьЗаголовок = Истина;
    ГруппаДляТЗ.РастягиватьПоГоризонтали = Истина;
    ГруппаДляТЗ.РастягиватьПоВертикали = Истина;

    // 2. Создаем элемент ТаблицаФормы
    ТаблицаФормы = Элементы.Добавить("ЭлементТаблица" + ИмяРеквизитаТаблицы, Тип("ТаблицаФормы"), ГруппаДляТЗ);
    ТаблицаФормы.Заголовок = "Таблица: " + ИмяРеквизитаТаблицы;
    ТаблицаФормы.ПутьКДанным = ИмяРеквизитаТаблицы;
    ТаблицаФормы.РастягиватьПоГоризонтали = Истина;
    ТаблицаФормы.РастягиватьПоВертикали = Истина;
    ТаблицаФормы.ИзменятьСоставСтрок = Истина;
    ТаблицаФормы.ВыделятьТекущуюСтроку = Истина;

    // 3. Создаем элементы ПолеФормы для каждой колонки
    Для Каждого ИмяКолонки Из СтрРазделить(СписокКолонокЧерезЗапятую, ",", Ложь) Цикл
        ИмяКолонки = СтрЗаменить(ИмяКолонки, " ", "");
        Если ПустаяСтрока(ИмяКолонки) Тогда Продолжить КонецЕсли;

        НоваяКолонка = Элементы.Добавить("ЭлементТаблица" + ИмяРеквизитаТаблицы + ИмяКолонки, Тип("ПолеФормы"), ТаблицаФормы);
        НоваяКолонка.Заголовок = ИмяКолонки;
        НоваяКолонка.ПутьКДанным = ИмяРеквизитаТаблицы + "." + ИмяКолонки;
        НоваяКолонка.Вид = ВидПоляФормы.ПолеВвода;
        НоваяКолонка.РастягиватьПоГоризонтали = Истина;
    КонецЦикла;

    // Теперь можно заполнить реквизит формы данными
    ЗаполнитьДанныеДляДинамическойТаблицы(ИмяРеквизитаТаблицы, СписокКолонокЧерезЗапятую);
КонецПроцедуры

&НаСервере
Процедура ЗаполнитьДанныеДляДинамическойТаблицы(ИмяРеквизитаТаблицы, СписокКолонокЧерезЗапятую)
    // Получаем ссылку на наш динамический реквизит формы
    ДинамическаяТЗ = ЭтаФорма[ИмяРеквизитаТаблицы];

    Если ДинамическаяТЗ = Неопределено Тогда
        // Этого не должно произойти, если СоздатьДинамическуюТаблицуНаФормеПоСписку была вызвана корректно
        ДинамическаяТЗ = Новый ТаблицаЗначений;
        ЭтаФорма[ИмяРеквизитаТаблицы] = ДинамическаяТЗ;
    КонецЕсли;

    ДинамическаяТЗ.Очистить();

    // Добавляем колонки в саму ТаблицуЗначений, если их там нет.
    // Это важно, так как ИзменитьРеквизиты() добавляет реквизиты формы,
    // но не гарантирует, что у самой ТаблицыЗначений есть эти колонки, 
    // если она была только что создана или очищена.
    Для Каждого ИмяКолонки Из СтрРазделить(СписокКолонокЧерезЗапятую, ",", Ложь) Цикл
        ИмяКолонки = СтрЗаменить(ИмяКолонки, " ", "");
        Если ПустаяСтрока(ИмяКолонки) Тогда Продолжить КонецЕсли;
        Если ДинамическаяТЗ.Колонки.Найти(ИмяКолонки) = Неопределено Тогда
            ОписаниеТипаКолонки = Новый ОписаниеТипов("Строка");
            Если ИмяКолонки = "Организация" Тогда
                ОписаниеТипаКолонки = Новый ОписаниеТипов("СправочникСсылка.Организации");
            ИначеЕсли ИмяКолонки = "Значение" Тогда
                ОписаниеТипаКолонки = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2));
            КонецЕсли;
            ДинамическаяТЗ.Колонки.Добавить(ИмяКолонки, ОписаниеТипаКолонки);
        КонецЕсли;
    КонецЦикла;
    
    // Пример заполнения данными
    Если ДинамическаяТЗ.Колонки.Найти("Показатель") <> Неопределено Тогда
        НоваяСтрока = ДинамическаяТЗ.Добавить();
        Если ДинамическаяТЗ.Колонки.Найти("Организация") <> Неопределено Тогда
            НоваяСтрока.Организация = Справочники.Организации.НайтиПоКоду("000000001");
        КонецЕсли;
        НоваяСтрока.Показатель = "Выручка за месяц";
        Если ДинамическаяТЗ.Колонки.Найти("Значение") <> Неопределено Тогда
            НоваяСтрова.Значение = 150000;
        КонецЕсли;
    КонецЕсли;

    Если ДинамическаяТЗ.Колонки.Найти("Показатель") <> Неопределено Тогда
        НоваяСтрока = ДинамическаяТЗ.Добавить();
        Если ДинамическаяТЗ.Колонки.Найти("Организация") <> Неопределено Тогда
            НоваяСтрока.Организация = Справочники.Организации.НайтиПоКоду("000000002");
        КонецЕсли;
        НоваяСтрока.Показатель = "Затраты на логистику";
        Если ДинамическаяТЗ.Колонки.Найти("Значение") <> Неопределено Тогда
            НоваяСтрока.Значение = 30000;
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

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

    // Подготавливаем массив для добавляемых реквизитов (будет заполнен на сервере)
    МассивДобавляемыхРеквизитов = Новый Массив;

    СоздатьДинамическуюТаблицуНаФормеПоСпискуНаСервере("ИмяРеквизитаТЗ", "Организация, Показатель, Значение", МассивДобавляемыхРеквизитов, Новый Массив);
    ВывестиДинамическуюТаблицуНаФормуПоСписку("ИмяРеквизитаТЗ", "Организация, Показатель, Значение", Элементы.СтраницаОсновное);
КонецПроцедуры

&НаСервере
Процедура СоздатьДинамическуюТаблицуНаФормеПоСпискуНаСервере(Знач ИмяРеквизитаТаблицы, Знач СписокКолонокЧерезЗапятую, Знач МассивДобавляемыхРеквизитов, Знач МассивУдаляемыхРеквизитов)
    СоздатьДинамическуюТаблицуНаФормеПоСписку(ИмяРеквизитаТаблицы, СписокКолонокЧерезЗапятую, МассивДобавляемыхРеквизитов, МассивУдаляемыхРеквизитов);
КонецПроцедуры

&НаСервере
Процедура ОчиститьДинамическуюТаблицуНаФормеНаСервере(МассивУдаляемыхРеквизитов)
    ИзменитьРеквизиты(Новый Массив, МассивУдаляемыхРеквизитов);
КонецПроцедуры

В этом примере мы:

  1. Создали функцию СоздатьДинамическуюТаблицуНаФормеПоСписку, которая принимает имя реквизита таблицы и строку с именами колонок через запятую. Это позволяет гибко менять состав колонок.
  2. Внутри этой функции мы определяем тип для каждой колонки. Обратите внимание: в реальном приложении логика определения типов колонок может быть значительно сложнее (например, брать из метаданных, получать по имени или из дополнительных параметров).
  3. В клиентской процедуре ВывестиДинамическуюТаблицуНаФормуПоСписку мы создаем группу, а затем саму таблицу и ее колонки.
  4. Процедура ЗаполнитьДанныеДляДинамическойТаблицы на сервере заполняет наш динамический реквизит формы данными. Критически важно убедиться, что колонки добавлены не только как реквизиты формы, но и в саму ТаблицуЗначений, если она была только что создана или очищена.

Шаг 4: Управление данными в динамической таблице

После того как реквизит формы типа ТаблицаЗначений создан и связан с элементом ТаблицаФормы, вы можете работать с ним как с обычной таблицей значений 1С. Обращение к нему осуществляется через объект формы:


// На сервере
ДинамическаяТаблица = ЭтаФорма.ИмяРеквизитаТЗ;

// Добавление строки
НоваяСтрока = ДинамическаяТаблица.Добавить();
НоваяСтрока.Организация = Справочники.Организации.МояОрганизация;
НоваяСтрока.Показатель = "Пример показателя";
НоваяСтрока.Значение = 123.45;

// Чтение данных
Для Каждого СтрокаТЗ Из ДинамическаяТаблица Цикл
    Сообщить(СтрокаТЗ.Показатель + ": " + СтрокаТЗ.Значение);
КонецЦикла;

// Очистка таблицы
ДинамическаяТаблица.Очистить();

Шаг 5: Повторное добавление и удаление: решение проблемы "Не уникальное имя реквизита"

Одна из самых распространенных проблем, с которой сталкиваются разработчики при динамическом изменении формы, это ошибка "Не уникальное имя реквизита" или "Не уникальное имя элемента" при попытке повторно создать уже существующие (но, возможно, удаленные визуально) элементы или реквизиты.

Причина проблемы:

  1. Метод Элементы.Удалить() удаляет только визуальное представление элемента с формы. Само описание реквизита формы (если это был реквизит формы, созданный через ИзменитьРеквизиты()) остается в памяти формы.
  2. При повторном вызове ИзменитьРеквизиты() с тем же именем реквизита, платформа обнаруживает, что реквизит с таким именем уже существует в описании формы, даже если его визуальный элемент отсутствует, и выдает ошибку.

Решение:

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


// В обработчике события или команде, где нужно обновить таблицу
&НаКлиенте
Процедура ОбновитьДинамическуюТаблицу(Команда)
    // 1. Сначала удаляем визуальные элементы с формы (клиент)
    Если Элементы.Найти("ГруппаДляДинамическойТЗ") <> Неопределено Тогда
        Элементы.Удалить(Элементы.ГруппаДляДинамическойТЗ);
    КонецЕсли;

    // 2. Затем удаляем реквизиты формы на сервере
    МассивУдаляемыхРеквизитов = Новый Массив;
    МассивУдаляемыхРеквизитов.Добавить("ИмяРеквизитаТЗ"); // Удаляем саму ТаблицуЗначений
    МассивУдаляемыхРеквизитов.Добавить("Организация"); // Удаляем реквизиты колонок
    МассивУдаляемыхРеквизитов.Добавить("Показатель");
    МассивУдаляемыхРеквизитов.Добавить("Значение");
    
    // Вызов серверной процедуры для удаления реквизитов формы
    УдалитьРеквизитыФормыДинамическойТаблицыНаСервере(МассивУдаляемыхРеквизитов);

    // 3. Теперь можно безопасно создать новые реквизиты и элементы
    // Подготовим массив для добавляемых реквизитов (будет заполнен на сервере)
    МассивДобавляемыхРеквизитов = Новый Массив;

    // Пример: создаем таблицу с другим набором колонок
    СоздатьДинамическуюТаблицуНаФормеПоСпискуНаСервере("ИмяРеквизитаТЗ", "Дата, Сумма", МассивДобавляемыхРеквизитов, Новый Массив);
    ВывестиДинамическуюТаблицуНаФормуПоСписку("ИмяРеквизитаТЗ", "Дата, Сумма", Элементы.СтраницаОсновное);

КонецПроцедуры

// Серверная процедура для удаления реквизитов формы
&НаСервере
Процедура УдалитьРеквизитыФормыДинамическойТаблицыНаСервере(МассивУдаляемыхРеквизитов)
    ИзменитьРеквизиты(Новый Массив, МассивУдаляемыхРеквизитов);
КонецПроцедуры

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

        ОписаниеТипаКолонки = Новый ОписаниеТипов("Строка");
        Если ИмяКолонки = "Дата" Тогда
            ОписаниеТипаКолонки = Новый ОписаниеТипов("Дата");
        ИначеЕсли ИмяКолонки = "Сумма" Тогда
            ОписаниеТипаКолонки = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2));
        КонецЕсли;

        МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы(ИмяКолонки, ОписаниеТипаКолонки, ИмяРеквизитаТаблицы, ИмяКолонки));
    КонецЦикла;

    ИзменитьРеквизиты(МассивДобавляемыхРеквизитов, Новый Массив); // Добавляем новые реквизиты
КонецПроцедуры

Основные моменты при удалении и повторном создании:

  1. Всегда удаляйте элементы формы с клиента с помощью Элементы.Удалить() перед повторным добавлением.
  2. Всегда удаляйте реквизиты формы на сервере с помощью ИзменитьРеквизиты(Новый Массив, МассивУдаляемыхРеквизитов) перед повторным добавлением реквизитов с теми же именами.
  3. Убедитесь, что массив удаляемых реквизитов содержит все имена реквизитов, которые были связаны с вашей динамической таблицей (саму таблицу и все ее колонки).
  4. Вызовы ИзменитьРеквизиты() всегда должны быть на сервере, даже если инициированы с клиента.

Шаг 6: Дополнительный контроль над таблицей (только просмотр, запрет добавления/удаления)

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

  1. Только просмотр для всей таблицы:

    Используйте свойство ТаблицаФормы.ТолькоПросмотр = Истина;. Это запретит редактирование, добавление и удаление строк.

    ТаблицаФормы.ТолькоПросмотр = Истина;
  2. Только просмотр для отдельных колонок:

    Для элемента ПолеФормы, соответствующего колонке, можно установить Вид = ВидПоляФормы.ПолеНадписи или ТолькоПросмотр = Истина.

    
    НоваяКолонка.Вид = ВидПоляФормы.ПолеНадписи;
    // или
    НоваяКолонка.ТолькоПросмотр = Истина;
    
  3. Запрет добавления/удаления строк с сохранением редактирования:

    Если вам нужно более тонкое управление, например, разрешить редактирование, но запретить добавление и удаление строк, вы можете использовать обработчики событий формы:

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

    Не забудьте привязать эти обработчики к вашему динамически созданному элементу ТаблицаФормы. Обычно это делается в конфигураторе, но для динамических элементов можно попытаться привязать их программно, хотя это сложнее и не всегда рекомендуется. Проще установить соответствующие свойства элемента ТаблицаФормы, такие как ИзменятьСоставСтрок = Ложь, что запретит добавление и удаление строк через стандартные кнопки.

    
    ТаблицаФормы.ИзменятьСоставСтрок = Ложь; // Запрещает кнопки "Добавить" и "Удалить"
    

Заключение

Программное создание реквизитов формы типа ТаблицаЗначений и элементов ТаблицаФормы с динамическими колонками на управляемой форме 1С – это мощный инструмент для создания гибких и адаптивных интерфейсов. Мы рассмотрели шаги по созданию реквизитов и элементов, их размещению, заполнению данными и, что особенно важно, по корректному удалению и повторному добавлению для предотвращения ошибок уникальности. Ключ к успеху заключается в понимании и управлении жизненным циклом как реквизитов формы на сервере, так и визуальных элементов на клиенте. Практикуйтесь с примерами, и вы сможете эффективно применять эти знания в своих разработках.

← На главную