Почему для реквизита шапки используется Объект.Реквизит, а для табличной части — Элементы.ТабличнаяЧасть.ТекущиеДанные?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При переходе к разработке на управляемых формах платформы «1С:Предприятие 8» многие разработчики сталкиваются с неочевидной на первый взгляд логикой работы с реквизитами. Возникает закономерный вопрос: почему для получения данных из табличной части мы обращаемся к коллекции Элементы (через Элементы.ИмяТаблицы.ТекущиеДанные.ИмяРеквизита), а для получения значения обычного реквизита шапки попытка написать Элементы.ИмяРеквизита приводит к ошибке? Почему в этом случае мы обязаны использовать структуру Объект.ИмяРеквизита?

В этой статье мы подробно разберем внутреннюю архитектуру управляемых форм, выясним причины такого разделения и рассмотрим практические приемы работы с данными на клиенте и сервере.


1. Архитектура управляемой формы: Разделение данных и представления

Для того чтобы понять логику платформы, проанализируем базовый паттерн проектирования, лежащий в основе управляемого приложения — Model-View-Controller (MVC). Разработчики управляемой платформы «1С» четко разделили форму на две независимые сущности:

  1. Данные формы (Модель): Это структура, которая хранит реальные значения реквизитов в оперативной памяти. На клиенте и сервере она представлена основным реквизитом формы (обычно это Объект типа ДанныеФормыСтруктура, либо напрямую реквизиты формы типа ДанныеФормыКоллекция, строки ДанныеФормыЭлементКоллекции и т.д.).
  2. Элементы формы (Представление): Это визуальные объекты (поля ввода, переключатели, флажки, декорации, группы), которые отображаются пользователю на экране. Они объединены в коллекцию Элементы.

Основное правило управляемого интерфейса: элементы формы не хранят внутри себя редактируемые данные. Они являются лишь «окнами» для отображения и редактирования данных, хранящихся в модели.

В старых «обычных» формах (платформа 1С 8.1 и ранее) у элементов управления было свойство Значение (например, ЭлементыФормы.ИмяРеквизита.Значение). В управляемых формах от этого архитектурного решения полностью отказались в целях оптимизации сетевого трафика между клиентом и сервером. Элементы формы на тонком и веб-клиенте знают только свои визуальные свойства: видимость, доступность, цвет, шрифт, положение на экране. Сами данные передаются отдельно в виде легких структур данных.


2. Почему для реквизитов шапки используется только «Объект»?

Рассмотрим детально реквизит шапки, например, Организация. На форме ему соответствуют:

Связь между ними устанавливается через свойство элемента ПутьКДанным (значение которого равно строке "Объект.Организация").

Так как реквизит шапки одиночный, связь здесь жесткая: «один реквизит — одно значение». Платформа всегда точно знает, где лежит значение этого реквизита — в структуре данных Объект. Обращаться к нему через интерфейсный элемент бессмысленно и технически невозможно, так как объект типа ПолеФормы не имеет свойств для непосредственного хранения или получения значения бизнес-данных.


// Правильно: получаем данные напрямую из модели
ТекущаяОрганизация = Объект.Организация;

// Неправильно: вызовет ошибку, у элемента формы нет свойства для получения значения
// ТекущаяОрганизация = Элементы.Организация.Значение; 

3. Почему для табличных частей нужны «Элементы»?

Ситуация с табличными частями принципиально иная. Табличная часть в модели данных — это коллекция строк (объект типа ДанныеФормыКоллекция), содержащая множество строк и колонок (например, Объект.Товары).

При этом на экране пользователя отображается всего один элемент управления — табличное поле типа ТаблицаФормы (в коллекции элементов это Элементы.Товары).

Выясним причину диссонанса: модель данных хранит все строки таблицы одновременно. Но когда пользователь совершает какое-либо действие (например, нажимает кнопку на панели или меняет значение в текущей строке), программе необходимо знать, с какой конкретно строкой сейчас взаимодействует пользователь.

Информация об активной строке (фокусе ввода, выделении) — это исключительно свойство интерфейса (отображения). Данные Объект.Товары ничего не знают о том, куда кликнул пользователь мышкой. Об этом знает только визуальный элемент управления — таблица формы.

Именно для этого у объекта ТаблицаФормы реализовано специальное свойство-мост — ТекущиеДанные. Оно обращается к интерфейсу, выясняет индекс активной строки и возвращает структуру данных (ДанныеФормыЭлементКоллекции) именно для этой конкретной строки.


// Мы запрашиваем у элемента интерфейса (Элементы.Товары) активную строку, 
// и получаем доступ к данным этой строки из модели
СтрокаТабличнойЧасти = Элементы.Товары.ТекущиеДанные;

Если СтрокаТабличнойЧасти <> Неопределено Тогда
    // Теперь мы можем читать и изменять реквизиты конкретной строки
    ТекущийТовар = СтрокаТабличнойЧасти.Номенклатура;
    СтрокаТабличнойЧасти.Количество = 10;
КонецЕсли;

Таким образом, использование коллекции Элементы для табличных частей — это вынужденная необходимость связывания многострочных данных с текущим фокусом внимания пользователя в интерфейсе.


4. Как динамически получить значение реквизита по элементу формы?

Иногда при разработке универсальных механизмов возникает задача: в общем клиентском обработчике (например, при изменении поля, куда передается параметр Элемент) динамически получить значение реквизита, связанного с этим элементом.

Рассмотрим несколько способов решения этой задачи и проанализируем их ограничения.

Способ А. По совпадению имен реквизита и элемента (Простой способ)

Если в форме имена элементов управления совпадают с именами реквизитов (что является стандартным поведением платформы при перетаскивании реквизитов на форму), мы можем использовать обращение через квадратные скобки к структуре данных:


&НаКлиенте
Процедура ИмяЭлементаПриИзменении(Элемент)
    // Получаем имя элемента (например, "Организация")
    ИмяРеквизита = Элемент.Имя;
    
    // Получаем значение из модели данных "Объект" по имени
    ЗначениеРеквизита = Объект[ИмяРеквизита];
    
    ПоказатьПредупреждение(, "Изменено значение реквизита: " + ЗначениеРеквизита);
КонецПроцедуры

Ограничение: Этот способ не сработает, если имя элемента на форме было изменено вручную и больше не совпадает с именем реквизита в объекте (или если путь к данным имеет сложную структуру, например, Объект.Контрагент.ОсновнойДоговор).

Способ Б. Вызов сервера для чтения «ПутьКДанным»

Свойство элемента формы ПутьКДанным хранит точный путь к переменной в модели данных. Однако на тонком и веб-клиенте это свойство недоступно (вызовет ошибку выполнения). Чтобы им воспользоваться, нам необходимо обратиться к серверному контексту:


&НаКлиенте
Процедура УниверсальныйОбработчикПриИзменении(Элемент)
    ЗначениеРеквизита = ПолучитьЗначениеЭлементаНаСервере(Элемент.Имя);
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьЗначениеЭлементаНаСервере(Знач ИмяЭлемента)
    // На сервере мы имеем полный доступ к свойствам элементов формы
    ТекущийЭлемент = Элементы[ИмяЭлемента];
    ПутьКДанным = ТекущийЭлемент.ПутьКДанным; // Вернет, например, "Объект.Организация"
    
    // Внимание! Использовать Вычислить() на сервере небезопасно, 
    // поэтому разберем путь программно или воспользуемся функциями БСП
    Возврат Вычислить(ПутьКДанным); 
КонецФункции

Важное примечание: Использование встроенной функции Вычислить() (Eval) крайне не рекомендуется в современных конфигурациях из соображений безопасности (особенно в веб-клиенте), а также из-за снижения производительности. Кроме того, на клиенте функция Вычислить() полностью отсутствует.

Способ В. Использование механизмов БСП (Рекомендуемый способ)

Если в вашей конфигурации используется Библиотека стандартных подсистем (БСП), для решения этой задачи предусмотрены готовые безопасные функции клиент-серверного общего модуля. Они позволяют корректно разбирать сложные пути к данным, включая точки:


&НаКлиенте
Процедура БезопасныйОбработчикПриИзменении(Элемент)
    // Получаем значение реквизита формы по его имени через БСП
    // Функция сама определит путь и безопасно извлечет данные
    ЗначениеРеквизита = ОбщегоНазначенияКлиентСервер.ПолучитьРеквизитФормыПоПути(ЭтотОбъект, "Объект." + Элемент.Имя);
КонецПроцедуры

Резюме

Подведем итоги нашего анализа архитектурных особенностей:

← На главную