Оглавление
В этой статье я рассмотрю пример использования подсистемы "Управление состоянием". Пример имеет учебный характер. В дальнейшем я планирую публиковать и другие учебные примеры. Целью каждого такого примера будет рассмотрение отдельных аспектов используемой подсистемы. Начну я с самого простого: отображение состояния формы с использованием функций состояния.
В этом примере мы рассмотрим основные шаги создания формы и построения её модели. Для начала мы используем один параметр состояния и зависимые от него элементы формы. Затем мы добавим еще один параметр состояния, зависимый от первого. Вначале мы будем использовать для его расчета выражение, а затем добавим функцию значения.
Также я акцентирую внимание на функции расчета модели состояния и управления формой. Первая, как и следует из её названия, служит для расчета параметров состояния, а вторая - для обновления формы. Особенность этого примера в том, что здесь будет использоваться функция расчета состояния модели формы, а не объекта.
В итоге мы построим обработку, где основная форма будет управляться через подсистему "Управление состоянием". На форме будет реализован нестандартный элемент управления, отображение которого будет управляться через подсистему. Сама модель будет реализована в общем модуле. В нем будут располагаться функции состояния и значения.
В примере используется нестандартный элемент формы, составленный из элементов декораций звезд двух видов: закрашенные и незакрашенные. Звезды располагаются в одну строку и вместе составляют шкалу звездного рейтинга от 0 до 5 закрашенных звезд. Состояние формы описывается одним параметром - Рейтинг. Рейтинг - это целое число от 0 до 5.
Необходимо реализовать такое поведение формы, при котором отображается шкала с числом закрашенных звезд равным величине выбранного рейтинга. При этом пользователь может выбрать одну из пяти звезд на шкале: если выбрана закрашенная звезда, то рейтинг понижается до уровня рейтинга выбранной звезды минус 1, если незакрашенная - рейтинг становится равным рейтингу звезды.
Для начала создадим обработку "Звездный рейтинг". У обработки будет один реквизит - Рейтинг (Число (1,0, положительное)).
Добавим основную форму обработки. В форме отключим автозаголовок и добавим 10 декораций типа Картинка: 5 закрашенных и 5 незакрашенных звезд. Имя декорации звезды сделаем таким, чтобы из него можно было бы получить информацию о рейтинге звезды и её состоянии закрашенная/незакрашенная. Для этого будем использовать следующий шаблон: ДекорацияЗвезда[Номер звезды][Состояние], где [Номер звезды] будет соответствовать величине рейтинга, а [Состояние] равно 1 - для закрашенной, а 0 - для незакрашенной звезды. На этом визуальная часть подготовки формы завершена.
Продолжим работу с формой в ее модуле. Для начала скопируем область кода "УправлениеСостоянием" из модуля формы документа ЗаявкаНаОперацию, который можно найти в демо конфигурации. Далее создадим обработчик события формы ПриСозданииНаСервере. В этом обработчике мы создадим описание модели формы.
Элементы формы вида полей ввода не нуждаются в дополнительном программировании, если необходимо реализовать изменение данных или расчет модели. Первое реализуется самой платформой стандартным поведением, второе - через подключение подсистемы "Управление состоянием". В нашем примере используется элемент формы вида декорация.
Поскольку элемент декорации звезды не является полем ввода, то стандартное взаимодействие с этим элементом не приведет к каким-либо изменениям данных. Исправим это, добавив в обработчик Нажатие код по изменению величины рейтинга в соответствии с нажатой звездой. Чтобы изменения отразились в модели используем функцию подсистемы РаботаСМодельюКлиентСервер.УстановитьЗначение для нового значения рейтинга. После внесения изменений нужно вызвать функцию УправлениеФормой, в которую необходимо передать массив измененных параметров состояния.
&НаКлиенте
Процедура ДекорацияЗвездаНажатие(Элемент)
Перем ИзмененныеРеквизиты;
// Определение состояния звезды
СоставИмениЭлементаЗвезды = СтрЗаменить(Элемент.Имя, "ДекорацияЗвезда", "");
СостояниеЗвезды = Прав(СоставИмениЭлементаЗвезды, 1);
РейтингЗвезды = Лев(СоставИмениЭлементаЗвезды, 1);
// Установка нового значения рейтинга по формуле: РейтингЗвезды - СостояниеЗвезды
РаботаСМодельюКлиентСервер.УстановитьЗначение(ЭтотОбъект, "Рейтинг",, РейтингЗвезды - СостояниеЗвезды, ИзмененныеРеквизиты);
// Обновление формы
УправлениеФормой(ИзмененныеРеквизиты);
// Возвращение фокуса на элемент звезды, которая была нажата
ЭтотОбъект.ТекущийЭлемент = Элементы[СтрШаблон("ДекорацияЗвезда%1%2", РейтингЗвезды, ?(СостояниеЗвезды = "1", "0", "1"))];
КонецПроцедуры
Обратите внимание, что как такового расчета модели пока в нашем примере нет. Действительно, что рассчитывать, если параметр всего один? Однако обновление формы требуется. Может показаться, что передача единственного параметра в функцию УправлениеФормой не имеет смысла, однако есть смысл передавать в функцию список параметров, которые изменились фактически. Если изменения не произойдет, то и обновления формы не будет - в этом и заключается один из главных аспектов оптимизации работы с формой.
Использование модели состояний предполагает наличие общего модуля, функции которого могут быть доступны как в режиме сервера, так и клиента. Для нашего примера добавим такой общий модуль "МодельЗвездныйРейтинг". Установим признаки доступности модуля: Клиент, Сервер.
В модуль модели добавим функции состояния элементов формы. В нашем примере необходимо добавить функции состояния для всех звезд и одну для формы. Формат описания функции строго определен следующим образом: Функция Свойство[ИмяЭлемента](Свойства, Параметры) Экспорт ... КонецФункции. Для самой формы имя элемента используется следующее: "ЭтаФорма".
В функциях состояния необходимо задать свойства элемента по параметрам, переданным в функцию. Используем для определения видимости закрашенной звезды параметр Рейтинг по следующему условию: Рейтинг > (Рейтинг звезды - 1). Для незакрашенной звезды это условие будет таким: Рейтинг < Рейтинг звезды.
Функция СвойстваЭтаФорма(Свойства, Параметры) Экспорт
Свойства.Вставить("Заголовок", СтрШаблон("Выбран рейтинг %1", Параметры.Рейтинг));
Возврат Истина;
КонецФункции
…
Функция СвойстваДекорацияЗвезда11(Свойства, Параметры) Экспорт
Свойства.Вставить("Видимость", Параметры.Рейтинг > 0);
Возврат Истина;
КонецФункции
…
Функция СвойстваДекорацияЗвезда10(Свойства, Параметры) Экспорт
Свойства.Вставить("Видимость", Параметры.Рейтинг < 1);
Возврат Истина;
КонецФункции
…
В этом примере в качестве объекта модели выступает форма. У формы единственный параметр состояния - Рейтинг. В системе нет необходимости описывать этот параметр отдельно. Также нет необходимости описывать какие-либо связи, т.к. других параметров просто нет.
Все что нам теперь остается - это добавить описание элементов формы в нашу модель. Формат описания предполагает перечисление элементов формы и параметров состояния, от которых они зависят.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Создание описания модели. Начало
Модель = РаботаСМодельюКлиентСервер.Модель("МодельЗвездныйРейтинг", "Объект");
// Описание самой формы
РаботаСФормойКлиентСервер.Элемент(ЭтотОбъект, Модель,, "Рейтинг");
// Закрашенные звезды
РаботаСФормойКлиентСервер.Элемент(ЭтотОбъект, Модель, Элементы.ДекорацияЗвезда11, "Рейтинг");
РаботаСФормойКлиентСервер.Элемент(ЭтотОбъект, Модель, Элементы.ДекорацияЗвезда21, "Рейтинг");
РаботаСФормойКлиентСервер.Элемент(ЭтотОбъект, Модель, Элементы.ДекорацияЗвезда31, "Рейтинг");
РаботаСФормойКлиентСервер.Элемент(ЭтотОбъект, Модель, Элементы.ДекорацияЗвезда41, "Рейтинг");
РаботаСФормойКлиентСервер.Элемент(ЭтотОбъект, Модель, Элементы.ДекорацияЗвезда51, "Рейтинг");
// Незакрашенные звезды
РаботаСФормойКлиентСервер.Элемент(ЭтотОбъект, Модель, Элементы.ДекорацияЗвезда10, "Рейтинг");
РаботаСФормойКлиентСервер.Элемент(ЭтотОбъект, Модель, Элементы.ДекорацияЗвезда20, "Рейтинг");
РаботаСФормойКлиентСервер.Элемент(ЭтотОбъект, Модель, Элементы.ДекорацияЗвезда30, "Рейтинг");
РаботаСФормойКлиентСервер.Элемент(ЭтотОбъект, Модель, Элементы.ДекорацияЗвезда40, "Рейтинг");
РаботаСФормойКлиентСервер.Элемент(ЭтотОбъект, Модель, Элементы.ДекорацияЗвезда50, "Рейтинг");
// Создание описания модели. Конец
РаботаСМоделью.Инициализировать(ЭтотОбъект, Модель);
// Отображение состояния формы
УправлениеФормойНаСервере();
КонецПроцедуры
Инициализация подсистемы в общем случае заключается в: создании структуры модели, определении топологического порядка параметров состояния, сохранении структуры модели в данных объекта, расчете производных (не сохраняемых) параметров состояния и только для формы - связывания элементов формы с моделью.
Вызов инициализации производится через единственную функцию подсистемы: Инициализация.
Для проверки работы полученной формы воспользуемся демо конфигурацией. Найти форму можно в меню Демо -> Сервис -> Звездный рейтинг. В конфигураторе это будет путь: Обработки->ЗвездныйРейтинг->ОсновнаяФорма.
Усложним наш пример. Введен еще один параметр состояния - Средний рейтинг. Пусть этот параметр условно рассчитывается по формуле: 0.5 * Рейтинг. Посчитанное значение будем отображать в заголовке формы.
…
РаботаСМодельюКлиентСервер.Связь(ЭтотОбъект, Модель, "СреднийРейтинг", "Рейтинг");
Модель.Параметры["СреднийРейтинг"].Выражение = "0.5 * Параметры.Рейтинг";
…
Для добавления параметра в нашу модель добавим новую связь Рейтинг -> Средний рейтинг. Отдельно описывать сам параметр обычно не требуется, и если в связи используется ранее неизвестный подсистеме параметр, то он автоматически добавляется в модель. Отдельно для нового параметра нам потребуется указать выражение его расчета.
На этом описание модели может быть завершено, однако, чтобы заголовок формы смог отображать его значение, нам необходимо в описание элемента ЭтаФорма добавить этот параметр.
…
РаботаСФормойКлиентСервер.Элемент(ЭтотОбъект, Модель,, "Рейтинг, СреднийРейтинг");
…
Затем в модуле модели для функции состояния ЭтаФорма нужно использовать новый параметр при формировании заголовка формы.
Функция СвойстваЭтаФорма(Свойства, Параметры) Экспорт
Свойства.Вставить("Заголовок", СтрШаблон("Выбран рейтинг %1, средний - %2", Параметры.Рейтинг, Параметры.СреднийРейтинг));
Возврат Истина;
КонецФункции
Запустим нашу обработку, откроем её форму и убедимся в её НЕработоспособности.
В главе "Взаимодействие" говорилось, что предыдущий пример не требует расчета модели, поэтому в нашем нестандартном элементе управления достаточно после изменения параметра Рейтинг просто вызвать функцию обновления формы УправлениеФормой (или, как принято у вебщиков - рендеринга). Однако во втором примере ситуация резко поменялась - у нас появилась связь, а значит есть зависимость, которую нужно рассчитать после изменения.
Для исправления ситуации достаточно заменить вызов функции обновления формы УправлениеФормой на функцию расчета состояния: РассчитатьСостояние, определенной также в контексте модуля формы.
Однако не всегда расчет параметра состояния возможно описать простым выражением. Усложним наш пример. Пусть средний рейтинг будет рассчитываться из данных рейтинга банков, значения которых хранятся в регистре сведений РейтингБанков.
Если выражение расчета не удается представить в простом виде, то можно использовать функцию значения. Добавим в нашу модель в общем модуле новую функцию значения для параметра "Средний рейтинг". Формат такой функции следующий: Функция Значение[Имя параметра](Контекст, Объект, Модель, Параметры, СтандартнаяОбработка) Экспорт. В самом параметре для выражения установим значение "*", что означает использование функции значения.
#Если Сервер Тогда
Функция ЗначениеСреднийРейтинг(Контекст, Объект, Модель, Параметры, СтандартнаяОбработка, Отказ) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| СУММА(РейтингБанков.Рейтинг) КАК СуммаРейтинга,
| КОЛИЧЕСТВО(РейтингБанков.Рейтинг) КАК КоличествоРейтинга
|ИЗ
| РегистрСведений.РейтингБанков КАК РейтингБанков";
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() И Выборка.КоличествоРейтинга > 0 Тогда
Возврат Окр((Выборка.СуммаРейтинга + Параметры.Рейтинг) / (Выборка.КоличествоРейтинга + 1), 2);
Иначе
Возврат Параметры.Рейтинг;
КонецЕсли;
КонецФункции
#КонецЕсли
Здесь проявляется еще один аспект работы подсистемы - требование контекста вычисления параметра состояния. Укажем этот контекст через признак параметра НаСервере = Истина.
...
РаботаСМодельюКлиентСервер.Связь(ЭтотОбъект, Модель, "СреднийРейтинг", "Рейтинг");
Модель.Параметры["СреднийРейтинг"].Выражение = "*";
Модель.Параметры["СреднийРейтинг"].НаСервере = Истина;
...
Вывод
- В модуль формы перенести область кода Управление состоянием из демо конфигурации
- Создать обработчик ПриСозданииНаСервере, в котором сделать описание модели формы. Добавить в обработчик:
- вызов инициализации подсистемы
- вызов обновления формы
- Добавить общий модуль модели, в котором разместить функции состояния и функции значения.
Вот так, на Раз, Два, Три мы подключили подсистему Управление состоянием к нашей форме.
Пример навеян следующими публикациями: