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