Почему пропадает текст в поле ввода при фоновой работе обработчика ожидания в 1С?

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

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

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

Анализ проблемы: почему очищается поле?

Чтобы понять корень проблемы, нужно проанализировать механизм взаимодействия клиента и сервера в платформе 1С при использовании контекстных серверных вызовов. Это процедуры и функции, объявленные с директивой &НаСервере.

Весь процесс можно разбить на несколько шагов:

  1. Ввод данных пользователем. Пользователь начинает вводить текст в поле на форме. В этот момент введенные символы находятся в элементе управления (визуальном поле), но еще не записаны в связанный с ним реквизит формы. Запись в реквизит происходит позже, например, при потере фокуса полем (когда пользователь кликает на другой элемент) или при нажатии Enter.
  2. Срабатывание обработчика ожидания. На клиенте срабатывает таймер, запущенный процедурой ПодключитьОбработчикОжидания, и вызывает назначенную процедуру.
  3. Контекстный вызов сервера. Внутри этой процедуры происходит вызов серверного метода, помеченного как &НаСервере. Это — ключевой момент. При таком вызове платформа неявно передает на сервер весь "контекст" формы — то есть, все ее реквизиты.
  4. Передача данных на сервер. Так как пользователь еще не закончил ввод и не перевел фокус, введенный им текст "лялялятополя" еще не попал в реквизит формы. Поэтому на сервер уезжает старое, пустое значение этого реквизита.
  5. Выполнение кода на сервере. Сервер выполняет свою работу. Для него реквизит, связанный с полем ввода, пуст.
  6. Возврат контекста и обновление формы. После завершения серверной процедуры платформа возвращает обновленный контекст формы обратно на клиент. Клиентское приложение получает эти данные и "перерисовывает" форму, чтобы привести ее в соответствие с данными, полученными с сервера.
  7. Очистка поля. Поскольку с сервера пришло пустое значение для реквизита, платформа обновляет и связанное с ним поле ввода, устанавливая в него пустое значение. Визуально это выглядит так, будто весь введенный пользователем текст просто стерли.

Вот минимальный пример кода, который воспроизводит эту проблему:


&НаКлиенте
Процедура ВключитьМониторингНовыхСообщений()
    ПодключитьОбработчикОжидания("ФоновоеОповещениеОНовыхСообщениях", 10);
КонецПроцедуры

&НаКлиенте
Процедура ФоновоеОповещениеОНовыхСообщениях()
    МассивЗаявокСОбновлениями = ФоновоеОповещениеОНовыхСообщенияхСервер();
КонецПроцедуры

&НаСервере
Функция ФоновоеОповещениеОНовыхСообщенияхСервер()
    МассивЗаявокСОбновлениями = Новый Массив;
    Возврат МассивЗаявокСОбновлениями;
КонецФункции

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

Решение 1: Бесконтекстный вызов сервера (Рекомендуемый способ)

Самый правильный и эффективный способ решения — отказаться от контекстного вызова в пользу бесконтекстного. Для этого используется директива &НаСервереБезКонтекста.

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

Как это реализовать:

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

Посмотрим на пример. Допустим, для работы серверной логики нам нужны значения двух реквизитов формы: Реквизит1 и Реквизит2. А в результате мы хотим обновить Реквизит3.


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

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

Преимущества этого подхода:

Решение 2: Настройка свойства поля ввода (Альтернативный способ)

Если по каким-то причинам переход на бесконтекстный вызов невозможен, можно воспользоваться настройками самого элемента формы. У поля ввода есть свойство "ОбновлениеТекстаРедактирования". Оно определяет, как и когда введенный текст синхронизируется с реквизитом данных.

Рассмотрим два варианта его использования:

  1. Установить в "При изменении значения"

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

    Минусы: Если на событие ПриИзменении для этого реквизита повешена какая-либо логика (особенно с серверными вызовами), она будет срабатывать на каждый символ. Это может серьезно замедлить работу интерфейса и создать лишнюю нагрузку на сервер.

  2. Установить в "Не использовать"

    Этот режим "отвязывает" визуальное отображение текста от автоматического обновления с сервера. Когда форма будет перерисована после контекстного вызова, платформа не станет трогать текст в поле ввода. Проблема очистки будет решена.

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

Использование этих свойств может быть быстрым решением, но оно часто влечет за собой побочные эффекты. Это скорее "обходной путь", чем полноценное решение.

Итог: какой способ выбрать?

Наиболее правильным, надежным и производительным решением является использование бесконтекстных серверных вызовов (&НаСервереБезКонтекста). Этот подход соответствует лучшим практикам разработки на платформе 1С, делает ваш код чище и избавляет от неожиданного поведения интерфейса.

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

← На главную