Почему ДеревоЗначений на управляемой форме выдает ошибку "Значение не является значением объектного типа" при повторном заполнении?

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

При работе с управляемыми формами в 1С:Предприятии 8.3, особенно когда мы только начинаем осваивать платформу, часто возникают сложности с пониманием того, как взаимодействуют объекты данных (например, ДеревоЗначений) и элементы формы, которые эти данные отображают. Давайте разберем распространенную проблему, когда при попытке обновить родительскую строку в ДеревеЗначений на форме после повторного заполнения возникает ошибка "Значение не является значением объектного типа". Мы выясним причину этой ошибки и предложим эффективное решение.

В чем заключается проблема?

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


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

    СтрокаРодитель = ДерЗнач.Строки.Добавить;
    СтрокаРодитель.Значение = "";
    СтрокаРодитель.Наименование = "";

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

Затем, по нажатию кнопки на форме, мы пытаемся обновить значения в родительской строке (например, подставить туда сумму значений из подчиненных строк и их количество). Изначально для поиска родительской строки используется код:


&НаКлиенте
Процедура СуммаИКоличествоДерЗнач(Команда)
    Тестовая = ЭтаФорма.ДеревоЗначений; // Получаем данные формы
    ДЗ = Тестовая.НайтиПоИдентификатору(0); // Пытаемся найти строку по идентификатору 0
    ДЗ.Значение = СуммаЗначений;
    ДЗ.Наименование = КоличествоЗначений;
КонецПроцедуры

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

Процедура очистки выглядит так:


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

Давайте подробно проанализируем, почему это происходит и как правильно работать с идентификаторами строк.

Разбираемся с механизмами 1С: Управляемые формы и ДеревоЗначений

Чтобы понять причину ошибки, нам необходимо углубиться в несколько ключевых концепций платформы 1С:Предприятие:

  1. Различие между объектом данных ДеревоЗначений и элементом формы ДеревоЗначений.

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

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

    • Процедура Очистить на сервере получает данные формы, полностью их очищает (`ДЗ.Строки.Очистить()`), а затем возвращает пустое дерево обратно на форму. Это действие сбрасывает все внутренние идентификаторы строк, которые были связаны с предыдущим состоянием дерева на форме. Когда мы повторно заполняем дерево, новые строки получают новые идентификаторы.

Выясняем причину ошибки "Значение не является значением объектного типа"

Теперь, когда мы понимаем механику, причина ошибки становится очевидной. В исходном коде:


ДЗ = Тестовая.НайтиПоИдентификатору(0);
ДЗ.Значение = СуммаЗначений; // Здесь ошибка

При первом запуске, возможно, случайно или по какой-то внутренней логике платформы, родительская строка получала идентификатор 0, и код работал. Однако после вызова процедуры Очистить и повторного заполнения, идентификаторы строк обновились. Родительская строка перестала иметь идентификатор 0. Соответственно, при повторном вызове Тестовая.НайтиПоИдентификатору(0) ничего не находится, и переменная ДЗ получает значение Неопределено. Любая попытка обратиться к свойству (например, .Значение) у переменной типа Неопределено приводит к ошибке "Значение не является значением объектного типа".

Важный момент: переменные СуммаЗначений и КоличествоЗначений, которые используются для обновления данных, являются реквизитами формы. Это означает, что они доступны как на клиенте, так и на сервере, и их значения сохраняются между вызовами серверных процедур, если они не были явно изменены или сброшены. Таким образом, они корректно передают данные.

Как правильно работать с ДеревомЗначений на управляемой форме: Корректное решение

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

Рассмотрим по шагам, как изменить код для корректного обновления родительской строки:

  1. Получаем идентификатор текущей строки с элемента формы.
    Если родительская строка является текущей (выделенной) строкой в элементе формы ДеревоЗначений (который в нашем случае оказался назван ДеревоЗначений1), мы можем получить ее идентификатор через свойство ТекущаяСтрока самого элемента формы.

    
    ИдентификаторТекущейСтроки = Элементы.ДеревоЗначений1.ТекущаяСтрока;
    
    Здесь Элементы.ДеревоЗначений1 – это ссылка на сам элемент управления "Дерево значений" на форме. Если бы ваш элемент назывался просто "ДеревоЗначений", то вы бы писали Элементы.ДеревоЗначений.ТекущаяСтрока.

  2. Находим соответствующую строку в данных формы.
    После того как мы получили актуальный ИдентификаторСтроки, мы можем использовать его с методом НайтиПоИдентификатору() у объекта данных формы ЭтаФорма.ДеревоЗначений. Этот метод вернет нам объект типа ДанныеФормыЭлементДерева, который представляет собой строку данных формы и позволяет работать с ее колонками.

    
    ДанныеТекущейСтроки = ЭтаФорма.ДеревоЗначений.НайтиПоИдентификатору(ИдентификаторТекущейСтроки);
    
    Обратите внимание, что мы обращаемся к ЭтаФорма.ДеревоЗначений, потому что именно этот реквизит формы является источником данных для элемента управления Элементы.ДеревоЗначений1.

  3. Обновляем значения колонок найденной строки.
    Теперь, когда у нас есть объект ДанныеТекущейСтроки, мы можем безопасно изменить значения его колонок:

    
    ДанныеТекущейСтроки.Значение = СуммаЗначений;
    ДанныеТекущейСтроки.Наименование = КоличествоЗначений;
    

Полный исправленный код процедуры на клиенте будет выглядеть так:


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

    // Проверяем, что строка действительно выбрана (ИдентификаторТекущейСтроки не Неопределено)
    Если ИдентификаторТекущейСтроки = Неопределено Тогда
        Сообщить("Не выбрана ни одна строка в дереве!");
        Возврат;
    КонецЕсли;

    // Шаг 2: Находим соответствующую строку в данных формы
    // "ДеревоЗначений" здесь - это реквизит формы, связанный с элементом "ДеревоЗначений1"
    ДанныеТекущейСтроки = ЭтаФорма.ДеревоЗначений.НайтиПоИдентификатору(ИдентификаторТекущейСтроки);

    // Шаг 3: Обновляем значения колонок найденной строки
    // СуммаЗначений и КоличествоЗначений - это реквизиты формы,
    // в которых хранятся рассчитанные значения.
    Если ДанныеТекущейСтроки <> Неопределено Тогда
        ДанныеТекущейСтроки.Значение = СуммаЗначений;
        ДанныеТекущейСтроки.Наименование = КоличествоЗначений;
    Иначе
        Сообщить("Не удалось найти строку с идентификатором: " + ИдентификаторТекущейСтроки);
    КонецЕсли;
КонецПроцедуры

Важные замечания и рекомендации

  1. Именование переменных: Как было отмечено в обсуждении, используйте осмысленные имена для переменных. Например, ИдентификаторТекущейСтроки вместо просто Индекс, и ДанныеТекущейСтроки вместо ДЗ. Это значительно улучшает читаемость и поддерживаемость кода.

  2. Имя элемента формы: Всегда внимательно проверяйте имя элемента управления на вашей форме. В данном случае, это было ДеревоЗначений1, а не ДеревоЗначений. Ошибка в имени элемента — частая причина проблем.

  3. Поиск родительской строки, если она не текущая: Если ваша задача состоит в том, чтобы всегда обновлять именно корневую родительскую строку, независимо от того, какая строка выбрана пользователем, подход с ТекущаяСтрока не будет универсальным. В таком случае вам потребуется:

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

  4. Работа с отладчиком: Как было предложено на форуме, используйте отладчик! Он является вашим лучшим другом при изучении платформы. Отслеживание значений переменных, таких как ДЗ или ИдентификаторТекущейСтроки, в процессе выполнения кода поможет вам понять, что происходит на каждом шаге и почему возникает ошибка.

Заключение

Мы видим, что проблема с ошибкой "Значение не является значением объектного типа" при работе с ДеревомЗначений на управляемых формах часто связана с недопониманием механизма ИдентификаторСтроки и различия между объектом данных и его представлением на форме. Запомните: ИдентификаторСтроки – это динамическое значение, присваиваемое платформой, и оно может меняться после очистки и повторного заполнения данных. Всегда получайте актуальный идентификатор строки из элемента формы (например, через Элементы.ИмяДерева.ТекущаяСтрока) перед тем, как использовать его для поиска данных в реквизите формы (ЭтаФорма.ИмяРеквизитаДерева.НайтиПоИдентификатору()). Использование этих принципов поможет вам избежать подобных ошибок и строить более надежные и предсказуемые решения на платформе 1С:Предприятие 8.3.

← На главную