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