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