Добавление колонок в ТаблицуЗначений: методы и код

· 9 мин

Содержание

Колонка в ТаблицаЗначений добавляется одной строкой: ТЗ.Колонки.Добавить("Имя"). Метод живёт не у самой таблицы, а у коллекции её колонок — свойства Колонки. Поэтому если искать у объекта метод с именем ДобавитьКолонку, его там нет: правильный вызов — ТЗ.Колонки.Добавить(...).

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

Код одинаково работает в обычном и управляемом приложении, на сервере и на клиенте — ТаблицаЗначений создаётся где угодно, кроме тонкого клиента без вызова на сервере.

Базовый вызов Колонки.Добавить

Создаём таблицу и добавляем колонки:

ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Номенклатура");
ТЗ.Колонки.Добавить("Количество");
ТЗ.Колонки.Добавить("Сумма");

Полная сигнатура метода:

ТЗ.Колонки.Добавить(<Имя>, <ТипЗначения>, <Заголовок>, <Ширина>)
  • Имя — идентификатор колонки, по нему обращаются в коде. Обязательный параметр.
  • ТипЗначения — объект ОписаниеТипов. Если не задать, колонка примет значение любого типа.
  • Заголовок — строка, которая показывается в шапке колонки на форме и в табличном документе.
  • Ширина — число, ширина колонки в символах при выводе.

Метод возвращает добавленную колонку, поэтому её свойства можно донастроить сразу:

Колонка = ТЗ.Колонки.Добавить("Цена");
Колонка.Заголовок = "Цена за единицу";
Колонка.Ширина = 12;

Имя и заголовок — разные вещи. В имени нельзя использовать пробелы и спецсимволы, оно подчиняется правилам идентификатора. Всё «человеческое» — пробелы, знаки, единицы измерения — выносите в заголовок.

Указание типа колонки через ОписаниеТипов

Чаще всего тип колонки нужно ограничить — тогда платформа контролирует вводимые значения и правильно показывает их на форме. Тип задаётся объектом ОписаниеТипов:

ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
ТЗ.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
ТЗ.Колонки.Добавить("Комментарий", Новый ОписаниеТипов("Строка"));

Для ссылочных типов имя пишется полностью:

ТЗ.Колонки.Добавить("Товар", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТЗ.Колонки.Добавить("Заказ", Новый ОписаниеТипов("ДокументСсылка.ЗаказКлиента"));

Квалификаторы: разрядность, длина, части даты

Голого имени типа часто мало. Для числа задают разрядность и число знаков после запятой, для строки — длину, для даты — её состав. Это делают квалификаторы:

// Число 15 знаков, 2 из них дробные
КвалЧисла = Новый КвалификаторыЧисла(15, 2);
ТЗ.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", КвалЧисла));

// Строка не длиннее 100 символов
КвалСтроки = Новый КвалификаторыСтроки(100);
ТЗ.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка", , КвалСтроки));

// Дата без времени
КвалДаты = Новый КвалификаторыДаты(ЧастиДаты.Дата);
ТЗ.Колонки.Добавить("ДатаОтгрузки", Новый ОписаниеТипов("Дата", , , КвалДаты));

Порядок параметров в строковом конструкторе ОписаниеТипов такой: сначала типы строкой, потом квалификатор числа, квалификатор строки и квалификатор даты. Пустые позиции пропускают запятой — поэтому у строки две запятые перед квалификатором, у даты — три.

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

Составные и неограниченные типы

Когда в колонке могут лежать значения разных типов, собираем составной тип. Строковый конструктор принимает несколько типов через запятую:

ТЗ.Колонки.Добавить("Значение", Новый ОписаниеТипов("Число, Строка, Дата"));

Либо через массив типов — так удобнее, когда типы вычисляются программно:

МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Число"));
МассивТипов.Добавить(Тип("Булево"));
СоставнойТип = Новый ОписаниеТипов(МассивТипов);
ТЗ.Колонки.Добавить("Флаг", СоставнойТип);

Составной тип нужен не только для «число или строка». Частый случай — колонка, в которой может лежать ссылка на документ любого вида: заказ, реализация, счёт. Тогда в тип добавляют несколько ссылочных типов:

ТЗ.Колонки.Добавить("Основание",
    Новый ОписаниеТипов("ДокументСсылка.ЗаказКлиента, ДокументСсылка.РеализацияТоваровУслуг"));

Если тип не указывать вовсе, колонка будет принимать значение любого типа — это и есть «неограниченный» вариант. Он удобен для временных и промежуточных таблиц, но на форме такая колонка редактируется хуже: платформа не знает, какой элемент управления подставить. Для данных, которые показываются пользователю, тип лучше задавать явно.

Имя и заголовок: обращение к колонке

К колонке в коде обращаются по имени — тремя способами, все равнозначны:

ТЗ.Колонки.Сумма.Заголовок = "Сумма, руб.";
ТЗ.Колонки["Сумма"].Заголовок = "Сумма, руб.";
ТЗ.Колонки.Найти("Сумма").Заголовок = "Сумма, руб.";

У колонки есть свойства Имя, Заголовок, Ширина, ТипЗначения, ПолныйЗаголовок, Формат и ФорматРедактирования. Переименовать колонку после создания нельзя — свойство Имя только для чтения. Если имя нужно поменять, добавляют новую колонку и переносят в неё значения, старую удаляют.

Имена колонок нечувствительны к регистру. Колонки Сумма и сумма считаются одной и той же — попытка добавить вторую вызовет ошибку. Это частая причина падения кода, который формирует имена колонок автоматически.

Добавление колонок программно в цикле

Классический сценарий — таблица с колонками по периодам, когда число периодов заранее неизвестно. Имена формируются на лету:

ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));

Для Месяц = 1 По 12 Цикл
    ИмяКолонки = "Месяц_" + Месяц;
    Заголовок = Формат(Дата(2024, Месяц, 1), "ДФ=MMMM");
    ТЗ.Колонки.Добавить(ИмяКолонки, Новый ОписаниеТипов("Число"), Заголовок);
КонецЦикла;

Имя здесь — служебное (Месяц_1, Месяц_2), а читаемое название месяца уходит в заголовок. Число приклеивается к строке оператором +, потому что Месяц — это целое без дробной части, лишних символов не добавит.

Заполнение уже существующих строк

Когда колонку добавляют к таблице, в которой уже есть строки, эти строки получают значение по умолчанию для указанного типа: ноль для числа, пустую строку для строки, пустую дату для даты. Если колонка без типа — значения будут Неопределено.

Нужное содержимое заполняют отдельным проходом:

ТЗ.Колонки.Добавить("Остаток", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));

Для Каждого Строка Из ТЗ Цикл
    Строка.Остаток = Строка.Приход - Строка.Расход;
КонецЦикла;

Быстрое заполнение колонки из массива

Когда значения колонки уже лежат в массиве, перебирать строки необязательно — есть метод ЗагрузитьКолонку. Он берёт массив и раскладывает его по строкам таблицы в порядке следования. Число элементов массива должно совпадать с числом строк:

ТЗ.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
ТЗ.ЗагрузитьКолонку(МассивЦен, "Цена");

Обратная операция — ВыгрузитьКолонку: возвращает массив значений одной колонки. Удобно, когда из таблицы нужно вытащить, например, все ссылки для запроса:

МассивТоваров = ТЗ.ВыгрузитьКолонку("Товар");

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

Само добавление колонки — операция дешёвая независимо от размера таблицы: меняется только описание структуры. Время съедает заполнение строк. Если колонок добавляется много и все — в цикле, выносите формирование ОписаниеТипов за пределы цикла, чтобы не пересоздавать его на каждой итерации.

Копирование структуры и перенос колонок между таблицами

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

СкопироватьКолонки возвращает новую таблицу — только структура, без строк:

ПустаяТЗ = ТЗ.СкопироватьКолонки();              // все колонки
ЧастичнаяТЗ = ТЗ.СкопироватьКолонки("Товар, Сумма"); // только эти две

Скопировать переносит и колонки, и строки; второй параметр ограничивает набор колонок:

КопияСДанными = ТЗ.Скопировать();                    // всё целиком
ДвеКолонки = ТЗ.Скопировать( , "Товар, Количество"); // строки есть, колонок две

Объединить колонки двух таблиц

Когда нужно добавить в одну таблицу колонки из другой — например, дополнить таблицу остатков колонками из таблицы цен, — перебирают коллекцию колонок источника и добавляют недостающие:

Для Каждого Колонка Из ТЗЦены.Колонки Цикл
    Если ТЗОстатки.Колонки.Найти(Колонка.Имя) = Неопределено Тогда
        ТЗОстатки.Колонки.Добавить(
            Колонка.Имя,
            Колонка.ТипЗначения,
            Колонка.Заголовок,
            Колонка.Ширина);
    КонецЕсли;
КонецЦикла;

Проверка через Найти обязательна: без неё при совпадении имён код упадёт на дубликате. Этот же приём собирает сводную таблицу из нескольких источников.

Вставка в позицию, порядок и удаление колонок

Добавить всегда ставит колонку в конец. Чтобы вставить её в начало или в середину, используют Вставить с индексом (нумерация с нуля):

ТЗ.Колонки.Вставить(0, "Период", Новый ОписаниеТипов("Дата")); // первой колонкой

Передвинуть уже существующую колонку:

ТЗ.Колонки.Сдвинуть("Сумма", -1); // на одну позицию влево

Удалить колонку можно по имени, по индексу или по самой колонке:

ТЗ.Колонки.Удалить("Цена");
ТЗ.Колонки.Удалить(0);
ТЗ.Колонки.Удалить(ТЗ.Колонки.Найти("Цена"));

Узнать число колонок — ТЗ.Колонки.Количество(), индекс колонки — ТЗ.Колонки.Индекс(Колонка). Порядок колонок в коллекции совпадает с порядком вывода на форме и в табличном документе.

Для ДеревоЗначений всё работает точно так же: у него тоже есть свойство Колонки с методом Добавить, те же параметры и те же правила по именам и типам. Разница только в том, что строки дерева образуют иерархию, а набор колонок — общий для всех уровней. Поэтому приёмы из этой статьи переносятся на дерево без изменений.

Проверьте себя
Где у объекта ТаблицаЗначений находится метод добавления колонки?
Куда писать отображаемое название колонки с пробелами и единицами измерения?
Что получат существующие строки после добавления колонки с типом Число?

Частые ошибки при добавлении колонок

Ошибки повторяются из проекта в проект — вот те, что встречаются чаще всего.

  • 🔁 Дубликат имени. «Колонка с таким именем уже существует» — имя уже занято, в том числе в другом регистре. Перед добавлением в цикле проверяйте Колонки.Найти(Имя) = Неопределено.
  • ✍️ Пробел или символ в имени. Имя — это идентификатор: только буквы, цифры и подчёркивание, первый символ не цифра. «Сумма, руб.» в имени недопустима — отправляйте такой текст в заголовок.
  • 🧩 Нет типа — странности на форме. Колонка без ОписаниеТипов принимает любой тип, но на управляемой форме редактируется плохо. Для видимых пользователю данных тип задавайте явно.
  • 🔢 Число без дробной части. Новый ОписаниеТипов("Число") без квалификатора округляет значения до целого. Для сумм — Новый КвалификаторыЧисла(15, 2), для количеств — (15, 3).
  • 🪟 Колонка на управляемой форме не появляется. Таблица на форме — это не ТаблицаЗначений, а данные формы. Добавление колонки в реквизит с типом ТаблицаЗначений на лету требует метода ИзменитьРеквизиты формы, иначе колонка в структуре есть, а в интерфейсе её нет.
Проверка перед добавлением колонки в цикле
Выполнено: 0 / 5

Частые вопросы

Можно ли добавить колонку в таблицу на управляемой форме во время работы?
Напрямую в данные формы — нет. Нужно менять реквизиты формы методом ИзменитьРеквизиты, передав ему добавляемые реквизиты-колонки, а затем связать их с элементом таблицы. Сами по себе колонки, добавленные в ТаблицаЗначений-реквизит обычным Колонки.Добавить, в интерфейсе не отобразятся.
Чем Скопировать отличается от СкопироватьКолонки?
СкопироватьКолонки возвращает новую таблицу только со структурой колонок, без строк. Скопировать переносит и колонки, и данные. Если нужна пустая таблица той же формы — берите СкопироватьКолонки.
Как задать колонке сразу несколько допустимых типов?
Соберите составной тип: Новый ОписаниеТипов("Число, Строка") строкой или через массив объектов Тип. Такая колонка примет значение любого из перечисленных типов.
Что будет, если имя колонки совпадёт с уже существующим?
Платформа выдаст ошибку о дубликате имени, выполнение прервётся. Имена нечувствительны к регистру, поэтому Сумма и сумма конфликтуют. Перед добавлением проверяйте Колонки.Найти(Имя).
Обязательно ли указывать тип колонки?
Нет. Без ОписаниеТипов колонка принимает значение любого типа. Для промежуточных расчётов это нормально, но для данных на форме тип лучше задать — платформа подберёт правильное поле ввода и проконтролирует значения.
Как перебрать имена всех колонок таблицы?
Коллекция Колонки обходится циклом Для Каждого: Для Каждого Колонка Из ТЗ.Колонки Цикл ... КонецЦикла, внутри доступно Колонка.Имя, Колонка.Заголовок, Колонка.ТипЗначения. Так удобно проверять структуру перед выгрузкой или сравнивать две таблицы по набору колонок.