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