Как исправить ошибку «Значение не является значением объектного типа (Types)» при поиске в программной таблице 1С?

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

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

Типичный пример такой проблемы — аварийное завершение работы системы при попытке пользователя воспользоваться стандартным поиском (сочетание клавиш Ctrl + F или кнопка «Найти» в меню «Ещё») по программно созданной таблице. Платформа генерирует крайне неприятное исключение: {mngbase/UniversalListFind.lf(90)}: Значение не является значением объектного типа (Types). В рамках данной статьи мы подробно разберем, почему возникает этот сбой, проанализируем поведение платформы и рассмотрим гарантированные способы решения этой проблемы.

Выясняем причину: почему происходит сбой в системном коде?

Для того чтобы понять природу ошибки, давайте заглянем «под капот» платформы 1С. Когда пользователь инициирует поиск по табличной части или таблице значений на форме, система должна отобразить диалоговое окно поиска. Это окно адаптивно: его поля ввода подстраиваются под тип данных той колонки, в которой в данный момент установлен курсор. Например, если активирована колонка с типом Дата, платформа выведет поле с календарем; если тип Число — поле с возможностью ввода чисел.

Чтобы определить, какой именно элемент управления показать в диалоге поиска, системный модуль UniversalListFind обращается к метаданным активной колонки. На уровне платформы происходит вызов метода Types() (Типы) у объекта ОписаниеТипов, связанного с реквизитом формы. Код системной библиотеки выглядит примерно так:


If (FinalValueType.Types().Count() = 1 And FinalValueType.ContainsType(Type("Date"))) Then
    // Системная настройка отображения поля даты
КонецЕсли;

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

Давайте проанализируем проверенные практикой способы обхода и устранения этого поведения.

Способ 1. Метод «служебной статической колонки» (Рекомендуемый)

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

Рассмотрим этот алгоритм по шагам:

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

Проанализируем пример кода, реализующий этот подход при перестроении колонок:


&НаСервере
Процедура ПерестроитьТаблицуНаФорме(НовыеКолонки)

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

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

Благодаря наличию постоянной колонки СлужебнаяКолонка, платформа 1С всегда имеет доступ к валидному описанию типов таблицы, что полностью предотвращает появление ошибки UniversalListFind при активации поиска.

Способ 2. Строгая типизация при создании динамических реквизитов

Иногда ошибка возникает из-за того, что разработчики пренебрегают явным указанием типов при создании реквизитов через конструктор Новый РеквизитФормы(). Если во второй параметр конструктора передать пустой объект или вовсе опустить его, платформа попытается инициализировать тип по умолчанию, что в динамической среде часто приводит к сбоям.

Проанализируем, как правильно инициализировать типы для динамических колонок, если они извлекаются из существующей на сервере ТаблицыЗначений:


&НаСервере
Процедура СоздатьКолонкиСЯвнойТипизацией(ИсходнаяТаблицаИсточника)

    ДобавляемыеРеквизиты = Новый Массив;
    
    Для Каждого КолонкаИсточника Из ИсходнаяТаблицаИсточника.Колонки Цикл
        
        // Извлекаем описание типов колонки-источника
        ТекущееОписаниеТипов = КолонкаИсточника.ТипЗначения;
        
        // Защита: если тип не определен, принудительно задаем строковый тип
        Если ТекущееОписаниеТипов.Типы().Count() = 0 Тогда
            МассивТипов = Новый Массив;
            МассивТипов.Добавить(Тип("Строка"));
            ТекущееОписаниеТипов = Новый ОписаниеТипов(МассивТипов);
        КонецЕсли;
        
        НовыйРеквизит = Новый РеквизитФормы(
            КолонкаИсточника.Имя, 
            ТекущееОписаниеТипов, 
            "ИмяРеквизитаТаблицыФормы", 
            КолонкаИсточника.Заголовок
        );
        ДобавляемыеРеквизиты.Добавить(НовыйРеквизит);
        
    КонецЦикла;
    
    ИзменитьРеквизиты(ДобавляемыеРеквизиты);

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

Использование строго типизированных объектов ОписаниеТипов значительно снижает вероятность того, что внутренний поисковый механизм платформы столкнется со значением Неопределено.

Способ 3. Использование строкового представления (Быстрый обходной путь)

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

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

Резюме и правила хорошего тона при динамической работе с формами

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

← На главную