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