Как правильно отреагировать на изменение ПоляНадписи (гиперссылки) в управляемой форме и задать вопрос пользователю?

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

При работе с управляемыми формами в системе 1С:Предприятие 8.3 мы часто сталкиваемся с необходимостью реагировать на изменения данных и взаимодействовать с пользователем. Один из таких сценариев возникает, когда значение элемента формы типа «ПолеНадписи», особенно если оно является гиперссылкой, изменяется, и нам нужно после этого задать вопрос пользователю. Давайте подробно разберем, как делать интерфейс в 1С, почему стандартное событие ПриИзменении в данном случае не срабатывает и как правильно организовать логику обработки изменений и вывода вопросов.

Понимание работы элемента «ПолеНадписи» и его событий

Начнем с анализа основного источника недопонимания: поведения элемента «ПолеНадписи», в отличие от того как работает модификатор управляемых форм. Этот элемент формы предназначен прежде всего для отображения текстовой информации. Он не является полем ввода данных, которые пользователь может интерактивно изменять.

  1. ПриИзменении для «ПолеНадписи» не работает: Мы должны понимать, что событие ПриИзменении для элементов формы в 1С:Предприятии срабатывает только тогда, когда пользователь интерактивно изменяет значение элемента, который предназначен для ввода данных (например, поле ввода, флажок, переключатель), где важна настройка доступности элементов формы. Если мы программно изменяем значение реквизита формы, к которому привязано «ПолеНадписи», или если изменяются данные другого элемента, влияющие на это «ПолеНадписи», событие ПриИзменении для самого «ПолеНадписи» не будет вызвано. Это логично, поскольку «ПолеНадписи» не предназначено для непосредственного ввода информации.
  2. Событие Нажатие для гиперссылок: Если наше «ПолеНадписи» настроено как гиперссылка (свойство «Гиперссылка» установлено в «Истина»), то при нажатии на него пользователем сработает событие Нажатие. Это событие предназначено для обработки кликов пользователя по ссылке, а не для отслеживания изменения отображаемых данных. Важно отметить, что событие Нажатие будет отрабатывать только если сама гиперссылка видна на форме, т.е. по заголовку элемента, если он не совпадает с содержимым надписи, оно не сработает.

Таким образом, пытаясь использовать ПриИзменении для «ПолеНадписи» при программном изменении данных, мы изначально идем по неверному пути. Нам необходимо отслеживать изменение исходного реквизита или элемента, который влияет на значение нашего «ПолеНадписи».

Организация отслеживания изменений и клиент-серверного взаимодействия

В нашей ситуации, когда изменение элемента ДоговорКонтрагента влечет за собой изменение данных ВидЦен (которое отображается в «ПолеНадписи»), мы должны реагировать именно на событие ПриИзменении элемента ДоговорКонтрагента. Внутри этого обработчика мы будем выполнять добавление элементов на форму и всю необходимую логику. Для стандартизации этого процесса можно использовать готовый общий модуль для программного изменения управляемых форм.

  1. Отслеживание изменения исходного элемента: Наш первый шаг — убедиться, что событие ПриИзменении для элемента ДоговорКонтрагента настроено и активно. Внутри этого обработчика мы будем вызывать серверную процедуру для обновления связанных данных.
  2. Серверная логика изменения данных: Поскольку изменение данных, скорее всего, затрагивает объекты базы данных или требует серверных вычислений, мы должны выполнить эту логику на сервере. Для этого мы создаем серверную процедуру или функцию, помеченную директивой &НаСервере или &НаСервереБезКонтекста. В этой процедуре мы изменяем реквизит, который связан с нашим «ПолеНадписи» ВидЦен.
  3. Возвращение признака на клиент: Ключевой момент – сервер не может напрямую взаимодействовать с интерфейсом пользователя (например, задавать вопросы). Серверная процедура должна вернуть на клиент некий признак (например, булево значение Истина или Ложь), указывающий, нужно ли задавать вопрос пользователю.
  4. Вывод вопроса на клиенте: После того как серверная процедура отработает и вернет результат на клиент, мы на стороне клиента анализируем этот результат. Если признак указывает на необходимость задать вопрос, мы используем соответствующие клиентские функции Вопрос() или ПоказатьВопрос().

Давайте рассмотрим этот процесс по шагам и с примером кода.

Пример организации клиент-серверного взаимодействия

Предположим, у нас есть реквизит формы ДоговорКонтрагента и реквизит формы ВидЦен, который связан с элементом «ПолеНадписи». При изменении ДоговорКонтрагента мы хотим обновить ВидЦен и, если условия соблюдены, спросить пользователя о дальнейших действиях.

Шаг 1: Обработчик ПриИзменении элемента ДоговорКонтрагента на клиенте

Это точка входа для нашей логики. Здесь мы инициируем процесс.


// В модуле управляемой формы
&НаКлиенте
Процедура ДоговорКонтрагентаПриИзменении(Элемент)
    // Объявляем переменную для хранения результата от сервера
    ПеременнаяДляВопроса = Ложь;

    // Вызываем серверную функцию для обновления ВидаЦен и определения, нужен ли вопрос
    Попытка
        ПеременнаяДляВопроса = ОбновитьВидЦенНаСервере();
    Исключение Э
        Сообщить("Ошибка при обновлении вида цен: " + Э.Описание);
        Возврат;
    КонецПопытки;

    // Если серверная функция вернула признак необходимости задать вопрос
    Если ПеременнаяДляВопроса Тогда
        // Задаем вопрос пользователю на клиенте.
        // Используем асинхронный вызов ПоказатьВопрос для неблокирующего интерфейса.
        ОписаниеОповещения = Новый ОписаниеОповещения("ОбработатьОтветНаВопрос", ЭтотОбъект);
        ПоказатьВопрос(ОписаниеОповещения, "Данные по виду цен изменились. Хотите выполнить дополнительные действия?", РежимДиалогаВопрос.ДаНет, , "Важное уточнение");
    КонецЕсли;

    // После обновления ВидаЦен, если это ПолеНадписи, нам нужно обновить его отображение
    // Если ВидЦен это реквизит формы, привязанный к ПолеНадписи, то оно обновится автоматически
    // Если же это просто текст, который мы устанавливаем программно, то нужно переустановить текст:
    // Элементы.ВидЦен.Заголовок = ЭтотОбъект.ВидЦен; // или другой способ обновления
КонецПроцедуры

Шаг 2: Серверная функция для обновления данных

Эта функция выполняет всю основную бизнес-логику и возвращает флаг, нужна ли реакция на клиенте.


// В модуле управляемой формы
&НаСервереБезКонтекста // Или &НаСервере, если нужен контекст формы/объекта
Функция ОбновитьВидЦенНаСервере()

    // Предположим, что реквизит формы ВидЦен уже привязан к элементу ПолеНадписи.
    // Если нужен контекст формы, используйте &НаСервере и обращайтесь к "РеквизитыОбъекта" или "ЭтотОбъект"
    // Пример с контекстом формы:
    // ЭтотОбъект.РеквизитВидЦен = "Новое значение цен";
    // Или, если это реквизит, не связанный с основным объектом формы:
    // ПеременныеФормы.ВидЦен = "Новый вид цен по договору";

    // Для примера без контекста, предположим, что мы передаем сюда необходимые данные
    // и возвращаем признак, основываясь на какой-то логике.
    // Например, если новый вид цен отличается от старого:
    // ИсходноеЗначениеВидЦен = ПолучитьВидЦенИзБазы(ПараметрДоговор);
    // НовоеЗначениеВидЦен = ОпределитьНовыйВидЦен(ПараметрДоговор);
    // Если НовоеЗначениеВидЦен <> ИсходноеЗначениеВидЦен Тогда
    //     // ЗаписатьНовоеЗначениеВидЦен(ПараметрДоговор, НовоеЗначениеВидЦен);
    //     Возврат Истина; // Нужно задать вопрос
    // КонецЕсли;

    // В нашем простом примере всегда возвращаем Истина,
    // чтобы показать механику вызова вопроса.
    Сообщить("Вид цен обновлен на сервере. Готовимся к вопросу на клиенте.");
    Возврат Истина; // Возвращаем булево значение на клиент
КонецФункции

Важные замечания по директивам:

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

Шаг 3: Процедура обработки ответа на вопрос

После того как пользователь ответит на вопрос, будет вызвана эта процедура на клиенте.


// В модуле управляемой формы
&НаКлиенте
Процедура ОбработатьОтветНаВопрос(РезультатВопроса, ДополнительныеПараметры)
    Если РезультатВопроса = КодВозвратаДиалога.Да Тогда
        Сообщить("Пользователь выбрал 'Да'. Выполняем дополнительные действия...");
        // Здесь можно вызвать другую клиентскую процедуру или серверную,
        // например, для открытия формы выбора или выполнения обработки.
        // Например: ОткрытьФормуВыбораНастроек();
    Иначе
        Сообщить("Пользователь выбрал 'Нет' или закрыл окно вопроса. Отмена дополнительных действий.");
        // Здесь можно реализовать отмену изменений или иную логику.
    КонецЕсли;
КонецПроцедуры

Функции для вывода сообщений и вопросов пользователю

В системе 1С:Предприятие существует несколько способов взаимодействия с пользователем через сообщения и вопросы. Мы рассмотрим основные из них.

Вывод информационных сообщений: СообщениеПользователю

Для простых информационных сообщений, которые не требуют ответа пользователя, мы можем использовать объект СообщениеПользователю.


// Пример использования СообщениеПользователю на клиенте
&НаКлиенте
Процедура ПоказатьИнформационноеСообщение()
    Сообщение = Новый СообщениеПользователю;
    Сообщение.Текст = "Информация успешно обновлена. Изменения применены.";
    Сообщение.Поле = "ВидЦен"; // Можно привязать сообщение к конкретному полю формы
    Сообщение.УстановитьИдентификатор("ОбновлениеВидаЦен"); // Для управления сообщениями
    Сообщение.Сообщить();
КонецПроцедуры

Также существуют универсальные процедуры в БСП (Библиотека стандартных подсистем), которые упрощают вывод сообщений с сервера или клиента:


// Пример использования ОбщегоНазначения.СообщитьПользователю на сервере (из БСП)
&НаСервере
Процедура ОбновитьДанныеИСказатьССервера()
    // ... логика на сервере ...
    ОбщегоНазначения.СообщитьПользователю("Данные по виду цен изменены на сервере!", "ВидЦен", );
КонецПроцедуры

// Пример использования ОбщегоНазначенияКлиент.СообщитьПользователю на клиенте (из БСП)
&НаКлиенте
Процедура ОбновитьДанныеИСказатьСКлиента()
    // ... логика на клиенте ...
    ОбщегоНазначенияКлиент.СообщитьПользователю("Данные по виду цен изменены на клиенте!", "ВидЦен");
КонецПроцедуры

Использование процедур БСП предпочтительнее, так как они обеспечивают единообразный вывод сообщений и часто включают дополнительную функциональность.

Вывод вопросов пользователю: Вопрос и ПоказатьВопрос

Когда нам нужен ответ от пользователя (например, «Да», «Нет», «Отмена»), мы используем функции вопросов. В управляемых формах есть два основных подхода:

  1. Синхронный вызов Вопрос(): Эта функция блокирует интерфейс пользователя до получения ответа. Она проста в использовании, но может негативно сказаться на пользовательском опыте, если вопрос задается часто или есть риск зависания.
    
    &НаКлиенте
    Процедура ЗадатьСинхронныйВопрос()
        Ответ = Вопрос("Вы уверены, что хотите продолжить?", РежимДиалогаВопрос.ДаНет);
        Если Ответ = КодВозвратаДиалога.Да Тогда
            Сообщить("Пользователь подтвердил.");
        Иначе
            Сообщить("Пользователь отменил.");
        КонецЕсли;
    КонецПроцедуры
    
  2. Асинхронный вызов ПоказатьВопрос() с ОписаниеОповещения: Этот метод является предпочтительным для управляемых форм, так как он не блокирует интерфейс. Мы передаем в ПоказатьВопрос() объект ОписаниеОповещения, который указывает, какую процедуру нужно вызвать после получения ответа. Мы уже использовали этот подход в нашем основном примере.
    
    &НаКлиенте
    Процедура ЗадатьАсинхронныйВопрос()
        ОписаниеОповещения = Новый ОписаниеОповещения("МойОбработчикОтвета", ЭтотОбъект);
        ПоказатьВопрос(ОписаниеОповещения, "Сохранить изменения перед закрытием?", РежимДиалогаВопрос.ДаНетОтмена);
    КонецПроцедуры
    
    &НаКлиенте
    Процедура МойОбработчикОтвета(РезультатВопроса, ДополнительныеПараметры)
        Если РезультатВопроса = КодВозвратаДиалога.Да Тогда
            Сообщить("Данные будут сохранены.");
        ИначеЕсли РезультатВопроса = КодВозвратаДиалога.Нет Тогда
            Сообщить("Данные не будут сохранены.");
        Иначе
            Сообщить("Действие отменено.");
        КонецЕсли;
    КонецПроцедуры
    

    Для более современного подхода с использованием оператора Ждать можно использовать функцию ВопросАсинх, которая возвращает результат выполнения ПоказатьВопрос:

    
    &НаКлиенте
    Процедура ЗадатьАсинхронныйВопросСЖдать()
        Ответ = Ждать(ВопросАсинх("Завершить процесс?", РежимДиалогаВопрос.ДаНет));
        Если Ответ = КодВозвратаДиалога.Да Тогда
            Сообщить("Процесс завершен.");
        Иначе
            Сообщить("Процесс продолжен.");
        КонецЕсли;
    КонецПроцедуры
    

Использование «ФорматированнойСтроки» для интерактивных надписей

В некоторых случаях, когда нам требуется более гибкая надпись, содержащая несколько интерактивных элементов (например, ссылки на разные объекты или различные части текста, которые должны реагировать на клик), мы можем использовать реквизит формы с типом «ФорматированнаяСтрока». При этом для элемента формы, привязанного к такому реквизиту, не нужно устанавливать свойство «Гиперссылка».

Элемент, привязанный к реквизиту типа «ФорматированнаяСтрока», будет иметь следующие события:

Пример программного формирования форматированной строки с ссылкой:


&НаСервере
Функция СформироватьФорматированнуюСтроку()
    // Создаем объект ФорматированнаяСтрока
    ФормСтрока = Новый ФорматированнаяСтрока;

    // Добавляем обычный текст
    ФормСтрока.Добавить("Текущий вид цен: ");

    // Добавляем ссылку на элемент справочника (например, "ВидыЦен")
    // Предположим, у нас есть ссылка на какой-то элемент ВидЦен
    СсылкаНаВидЦен = Справочники.ВидыЦен.НайтиПоКоду("00001");
    Если СсылкаНаВидЦен <> Неопределено Тогда
        ТекстСсылки = "Основной вид";
        ФормСтрока.Добавить(ТекстСсылки, , "НавигационнаяСсылка", СсылкаНаВидЦен.ПолучитьНавигационнуюСсылку());
    Иначе
        ФормСтрока.Добавить("Вид не найден.");
    КонецЕсли;

    Возврат ФормСтрока;
КонецФункции

&НаКлиенте
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // РеквизитФормы.ВидЦенФорматированнаяСтрока имеет тип ФорматированнаяСтрока
    ЭтотОбъект.ВидЦенФорматированнаяСтрока = СформироватьФорматированнуюСтроку();
КонецПроцедуры

&НаКлиенте
Процедура ВидЦенФорматированнаяСтрокаНажатие(Элемент, СтандартнаяОбработка)
    Сообщить("Пользователь нажал на форматированную строку!");
    // Дополнительная логика обработки нажатия
    // Например, можно получить текст ссылки, которую нажали
    // СтандартнаяОбработка = Ложь; // Если хотим перехватить стандартное поведение
КонецПроцедуры

&НаКлиенте
Процедура ВидЦенФорматированнаяСтрокаОбработкаНавигационнойСсылки(Элемент, Ссылка, СтандартнаяОбработка)
    Сообщить("Пользователь нажал на навигационную ссылку: " + Ссылка);
    // Здесь мы можем перехватить стандартный переход по ссылке
    // и выполнить свою логику, например, открыть форму с дополнительными параметрами
    // СтандартнаяОбработка = Ложь; // Если хотим перехватить стандартное поведение
КонецПроцедуры

Программная установка обработчика событий

Хотя в нашей конкретной задаче, связанной с ПриИзменении для «ПолеНадписи», это не является прямым решением, полезно знать, что обработчики событий для элементов формы могут быть установлены программно. Это делается с помощью процедуры УстановитьДействие(). Для практического освоения этой механики подойдёт пример программного создания списка кнопок с динамическим назначением обработчика событий.


// Пример программной установки обработчика для кнопки
&НаКлиенте
Процедура ПриОткрытии(Отказ)
    // Допустим, у нас есть кнопка "МояКнопка"
    Если Элементы.МояКнопка <> Неопределено Тогда
        Элементы.МояКнопка.УстановитьДействие("Нажатие", "МояКнопкаНажатие");
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура МояКнопкаНажатие(Элемент)
    Сообщить("Кнопка была нажата программно установленным обработчиком!");
КонецПроцедуры

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

Заключение

Мы выяснили, что для элемента «ПолеНадписи» в управляемых формах 1С:Предприятия 8.3 событие ПриИзменении не срабатывает при программном изменении связанных данных, так как это элемент отображения, а не ввода. Вместо этого мы должны отслеживать изменения основного реквизита, который влияет на отображаемое значение.

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

← На главную