Почему исчезает текст в поле ввода 1С при выборе значения из списка и как это исправить

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

В практике разработки на платформе 1С:Предприятие 8.3 часто встречается неприятная ситуация: в управляемой форме создается реквизит с типом Произвольный, который отображается в виде поля ввода с режимом выбора из списка. Разработчик наполняет список выбора парами "Представление" (строка) и "Значение" (например, ФиксированнаяСтруктура). На первый взгляд все работает отлично: пользователь выбирает элемент, в поле красуется нужный текст, а в реквизит записывается структура. Однако стоит форме совершить любой "поход на сервер" (round-trip) — например, при изменении другого поля или выполнении команды — как текст в поле ввода бесследно исчезает, хотя само значение реквизита остается на месте.

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

Выясняем причину: механизмы сериализации и визуальная связь

Разберем ситуацию изнутри. Когда мы работаем с управляемыми формами, платформа постоянно синхронизирует данные между клиентом и сервером. При любом серверном вызове контекст формы (ее реквизиты) сериализуется, передается на сервер, обрабатывается и возвращается обратно в десериализованном виде.

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

Подвох кроется в сравнении объектов: после возвращения с сервера ФиксированнаяСтруктура, полученная в результате десериализации, с точки зрения платформы может не быть идентичной тому объекту, который был помещен в СписокВыбора изначально. Визуальная связь разрывается, платформа "не узнает" значение в списке и, как следствие, не может сопоставить ему текстовое представление. Поле ввода выглядит пустым, хотя в данных формы структура по-прежнему присутствует.

Решение №1: Использование клиентского кэша и строковых ключей

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

Рассмотрим по шагам реализацию этого подхода. Сначала объявим переменную на клиенте, которая будет хранить наши данные в течение времени жизни формы, и настроим заполнение списка.

Проанализируем пример кода:


&НаКлиенте
Перем ВариантВыходаПродукцииХранение; // Кэш данных на клиенте

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

&НаСервере
Функция ЗаполнитьВариантыНаСервере()
    Запрос = Новый Запрос("ВЫБРАТЬ Мука, Отруби ИЗ РегистрСведений.ВариантыВыхода");
    Результат = Запрос.Выполнить().Выбрать();
    
    // Создаем соответствие для хранения структур на клиенте
    Кэш = Новый Соответствие;
    Список = Элементы.ВариантВыхода.СписокВыбора;
    Список.Очистить();
    
    Пока Результат.Следующий() Цикл
        // Формируем строковое представление, оно же будет ключом
        Текст = "Мука: " + Результат.Мука + "%, Отруби: " + Результат.Отруби + "%";
        СтруктураДанных = Новый ФиксированнаяСтруктура("Мука, Отруби", Результат.Мука, Результат.Отруби);
        
        // В список выбора добавляем только СТРОКУ в значение и представление
        Список.Добавить(Текст);
        Кэш.Вставить(Текст, СтруктураДанных);
    КонецЦикла;
    
    Возврат Кэш;
КонецФункции

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


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

Преимущества этого метода: текст в поле ввода стабилен, так как значением реквизита является сама строка текста. Платформе не нужно выполнять сложное сопоставление объектов после серверного вызова.

Решение №2: Хранение индекса массива

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

Посмотрим на алгоритм реализации этого метода:

  1. Создаем на форме скрытый реквизит ДанныеВыбораСписок с типом ДанныеФормыКоллекция или используем переменную модуля на клиенте с типом Массив.
  2. В СписокВыбора поля ввода добавляем элементы, где Значение — это порядковый номер (0, 1, 2...), а Представление — нужный пользователю текст.
  3. Реквизит формы, связанный с полем, делаем типом Число.
  4. При изменении значения обращаемся к массиву по индексу: НужныеДанные = МойМассив[Индекс];.

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

Решение №3: Сериализация в JSON (простой тип)

Если вы не хотите поддерживать переменные модуля или дополнительные коллекции, можно прибегнуть к "хитрости" с сериализацией данных. Вместо того чтобы помещать в значение СписокВыбора объект ФиксированнаяСтруктура, превратите его в строку JSON.

Проанализируем, как это работает:

Поскольку значение теперь — обычная строка (хоть и длинная, в формате JSON), платформа 1С идеально отрабатывает ее визуализацию. В обработчике ПриИзменении вы просто десериализуете строку JSON обратно в структуру для работы. Это позволяет избежать создания дополнительных клиентских переменных и делает форму более автономной.

Почему не стоит использовать "ПриОбновленииОтображения"

Некоторые разработчики пытаются решить проблему "в лоб", насильно переписывая текст в поле ввода через обработчик Ожидание или ПриОбновленииОтображения. Мы не рекомендуем этот путь по нескольким причинам:

  1. Производительность: Обработчик ПриОбновленииОтображения вызывается крайне часто. Лишний код в нем замедляет работу интерфейса.
  2. Артефакты: Текст может "моргать" при обновлении формы, что создает ощущение нестабильности программы.
  3. Сложность поддержки: Вам придется вручную описывать логику поиска представления для каждого сложного поля на форме.

Итоги

Подводя итог, можно сказать, что главная ошибка при работе с типом Произвольный в списках выбора — это ожидание от платформы корректной десериализации сложных объектов в элементах управления. Для стабильной работы интерфейса всегда старайтесь сводить Значение (Value) элемента СписокВыбора к примитивным типам: Строка или Число.

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

← На главную