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