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