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