При разработке на платформе 1С:Предприятие 8.3 в режиме управляемых форм программисты часто сталкиваются с необходимостью динамического изменения интерфейса. Типичная задача — программно добавить новую колонку (реквизит) в существующую табличную часть документа непосредственно во время работы приложения. Это позволяет выводить вспомогательную или расчетную информацию без изменения структуры метаданных самой конфигурации. Практические приёмы такой работы демонстрирует обработка с примером динамического создания и удаления элементов формы.
Однако при попытке связать программно созданный элемент формы с новым путем к данным разработчики нередко натыкаются на труднообъяснимую ошибку: «Ошибка при установке значения атрибута контекста (ПутьКДанным)» по причине: Недопустимое значение. Выясним причину этого поведения платформы и детально разберем рабочие способы решения проблемы.
Для оптимизации клиент-серверного взаимодействия в управляемом приложении 1С используется интеллектуальный механизм кэширования и минимизации сетевого трафика. Платформа анализирует структуру формы, спроектированную в конфигураторе, и передает с сервера на клиент данные только тех колонок табличных частей, которые физически выведены на экран в виде элементов формы. За это отвечает системное свойство реквизитов «Использовать всегда».
Если для табличной части в конфигураторе это свойство отключено (или снят флаг у конкретных колонок), платформа считает, что передавать «лишние» данные на клиент нет необходимости. Когда мы динамически добавляем новый реквизит в коллекцию формы с помощью метода ИзменитьРеквизиты(), этот реквизит создается внутри родительской табличной части. Он автоматически наследует значение флага «Использовать всегда» от родителя. Если у родительской таблицы этот флаг снят, то и наш динамический реквизит блокируется для передачи на клиент.
В момент выполнения строки кода, связывающей элемент формы с данными:
НовыйЭлементФормы.ПутьКДанным = "Объект.ДанныеОВремени.ВремяИтогСтарое";
Платформа генерирует исключение, поскольку указанный путь к данным считается недоступным для клиентской части формы из-за оптимизации.
Многие разработчики, пытаясь решить проблему программно, находят в синтакс-помощнике метод УстановитьОбязательноеИспользование(). Однако попытка применить его к табличной части документа или к ее элементам на форме вызывает ошибку «Метод объекта не обнаружен».
Проанализируем эту ситуацию. Данный метод принадлежит исключительно классу ДанныеФормыСтруктураСДинамическимСписком (динамическим спискам). Для обычных табличных частей документов, которые на форме представлены типом ДанныеФормыКоллекция, этого метода не существует. Платформа 1С не предоставляет программного интерфейса для изменения свойства «Использовать всегда» у стандартных реквизитов формы в режиме выполнения (runtime).
Поскольку напрямую программно установить этот флаг невозможно, рассмотрим два надежных обходных пути решения этой задачи.
Если вы работаете с типовой конфигурацией (например, «1С:Зарплата и управление персоналом 3.1») и не хотите снимать ее с поддержки, наилучшим решением будет использование механизма расширений. Это позволит нам один раз «вручную» установить нужное свойство в конфигураторе расширения, после чего программный код заработает без ошибок.
Разберем этот процесс по шагам:
Табель) и добавьте его форму в расширение.Объект и найдите вашу табличную часть (например, ДанныеОВремени).F4).После этого изменения родительская табличная часть всегда будет передавать структуру своих подчиненных реквизитов на клиент. Теперь ваш исходный программный код по добавлению колонки выполнится корректно:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Шаг 1. Создаем массив добавляемых реквизитов
ДобавляемыеРеквизиты = Новый Массив;
НовыйРеквизит = Новый РеквизитФормы(
"ВремяИтогСтарое",
Новый ОписаниеТипов("Строка"),
"Объект.ДанныеОВремени",
"Время итог (старое)"
);
ДобавляемыеРеквизиты.Добавить(НовыйРеквизит);
// Шаг 2. Изменяем структуру реквизитов формы
ЭтаФорма.ИзменитьРеквизиты(ДобавляемыеРеквизиты);
// Шаг 3. Добавляем визуальный элемент на форму
ЭлементТаблица = Элементы.ДанныеОВремени;
НовыйЭлемент = Элементы.Добавить("ВремяИтогСтарое", Тип("ПолеФормы"), ЭлементТаблица);
НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
НовыйЭлемент.ПутьКДанным = "Объект.ДанныеОВремени.ВремяИтогСтарое";
НовыйЭлемент.ТолькоПросмотр = Истина;
КонецПроцедуры
Полезный совет: Чтобы сделать код еще более лаконичным, вы можете добавить саму колонку ВремяИтогСтарое непосредственно в форму расширения визуально в конфигураторе. В этом случае отпадает необходимость писать сложный программный код добавления реквизитов на сервере. Для настройки формы без изменения кода также можно использовать инструмент для добавления реквизитов и элементов без программирования.
Бывают ситуации, когда использование расширений невозможно — например, при разработке универсальной внешней обработки или отчета, которые должны запускаться в любой базе без предварительной настройки конфигурации. В этом случае мы не можем гарантировать, что у табличной части документа включено свойство «Использовать всегда».
Чтобы обойти это ограничение, мы можем применить архитектурный паттерн с использованием временной программной таблицы значений на форме. Рассмотрим этот алгоритм:
ТаблицаЗначений. Для полностью созданных в коде таблиц оптимизация «Использовать всегда» не накладывает ограничений на привязку путей к данным.Объект.ДанныеОВремени во вновь созданную таблицу значений и добавляем нашу пользовательскую колонку ВремяИтогСтарое.Рассмотрим пример реализации создания такой таблицы:
&НаСервере
Процедура ИнициализироватьПрограммнуюТаблицу()
// 1. Описываем коллекцию реквизитов для новой таблицы
РеквизитыДляДобавления = Новый Массив;
// Создаем саму таблицу на форме
РеквизитыДляДобавления.Добавить(Новый РеквизитФормы("ПрограммнаяТаблица", Новый ОписаниеТипов("ТаблицаЗначений")));
// Копируем колонки из оригинальной ТЧ документа
Для Каждого Колонка Из Метаданные.Документы.Табель.ТабличныеЧасти.ДанныеОВремени.Реквизиты Цикл
РеквизитыДляДобавления.Добавить(Новый РеквизитФормы(Колонка.Имя, Колонка.Тип, "ПрограммнаяТаблица"));
КонецЦикла;
// Добавляем нашу кастомную колонку, которой нет в метаданных
РеквизитыДляДобавления.Добавить(Новый РеквизитФормы("ВремяИтогСтарое", Новый ОписаниеТипов("Строка"), "ПрограммнаяТаблица"));
// Применяем изменения на форме
ЭтаФорма.ИзменитьРеквизиты(РеквизитыДляДобавления);
// 2. Создаем элемент управления "Таблица" на форме
НоваяТаблицаФормы = Элементы.Добавить("ТаблицаВспомогательная", Тип("ТаблицаФормы"));
НоваяТаблицаФормы.ПутьКДанным = "ПрограммнаяТаблица";
// 3. Создаем элементы-колонки для отображения на форме
Для Каждого Реквизит Из РеквизитыДляДобавления Цикл
Если Реквизит.Путь = "ПрограммнаяТаблица" Тогда
НоваяКолонка = Элементы.Добавить("Колонка_" + Реквизит.Имя, Тип("ПолеФормы"), НоваяТаблицаФормы);
НоваяКолонка.Вид = ВидПоляФормы.ПолеВвода;
НоваяКолонка.ПутьКДанным = "ПрограммнаяТаблица." + Реквизит.Имя;
КонецЕсли;
КонецЦикла;
// Заполняем данными из физического объекта
ЗначениеВРеквизитФормы(Объект.ДанныеОВремени.Выгрузить(), "ПрограммнаяТаблица");
КонецПроцедуры
Этот способ более трудоемок в реализации, так как требует дополнительно описывать процедуры синхронизации данных при записи (через вызов метода РеквизитФормыВЗначение) и обработку интерактивных действий пользователя. Однако он гарантирует 100% автономность работы вашего программного кода независимо от настроек метаданных основной конфигурации.