При проектировании интерфейсов в среде «1С:Предприятие 8.3» на управляемых формах разработчики часто сталкиваются со следующей задачей: необходимо предоставить пользователю поле ввода, в которое можно либо выбрать существующий документ из базы данных (тип ДокументСсылка), либо просто ввести любое текстовое наименование вручную (например, если документа физически нет в базе данных и он существует только в бумажном виде).
Стандартное поведение платформы при настройке составного типа реквизита (ссылка + строка) доставляет пользователям массу неудобств. При любой попытке начать ввод текста или очистить поле 1С открывает модальное окно «Выбор типа данных», заставляя пользователя лишний раз кликать мышкой по пункту «Строка». Проанализируем, почему так происходит, и разберем несколько практических способов реализации удобного ввода без назойливых всплывающих окон.
Когда реквизит базы данных имеет составной тип (например, ссылочные типы документов совместно с типом Строка), платформа на уровне СУБД выделяет под это поле особый формат хранения. На форме такое поле ввода по умолчанию ведет себя строго: оно должно точно определить, данные какого именно типа пользователь собирается ввести.
Если мы не переопределяем стандартные механизмы, то при активации поля ввода платформа всегда будет требовать явного указания типа. Рассмотрим, какими методами мы можем исправить этот недостаток и сделать работу пользователей комфортной.
Если нам нужно решить задачу максимально быстро и без глубокого переопределения стандартных механизмов платформы, мы можем воспользоваться интерактивной настройкой свойств элемента формы. Выясним, как настроить выпадающий список для быстрого переключения типов.
Для этого выполним следующие шаги:
После этого при открытии формы нам необходимо программно инициализировать реквизит пустой строкой, чтобы платформа сразу перевела поле в режим текстового ввода. В модуле формы напишем следующий код:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Если это новый объект или реквизит не заполнен, инициализируем его пустой строкой
Если Не ЗначениеЗаполнено(Объект.НаименованиеДокумента) Тогда
Объект.НаименованиеДокумента = "";
КонецЕсли;
КонецПроцедуры
Результат: Поле по умолчанию предлагает вводить текст. Если пользователю потребуется выбрать документ-ссылку, он сможет нажать на кнопку выбора («...») или выбрать тип через выпадающий список. Однако этот вариант все еще не идеален с точки зрения юзабилити, так как при полной очистке поля платформа может снова сбросить тип на неопределенный.
Рассмотрим более элегантный профессиональный подход. Его суть заключается в том, что для элемента формы (поля ввода) мы принудительно ограничиваем доступный тип только строкой, но программно перехватываем события выбора и автоподбора. Таким образом, визуально для пользователя поле всегда остается обычной строкой, но мы сохраняем возможность подбора реальных документов.
Разберем реализацию этого метода по шагам.
Шаг 1. Ограничение типа элемента формы при создании
В обработчике события ПриСозданииНаСервере мы программно задаем тип элемента формы, ограничивая его только строкой. Это раз и навсегда заблокирует появление окна «Выбор типа данных».
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Строка"));
// Ограничиваем тип только строкой с максимальной длиной 200 символов
ОписаниеСтроковогоТипа = Новый ОписаниеТипов(МассивТипов, , Новый КвалификаторыСтроки(200));
Элементы.НаименованиеДокумента.ОграничениеТипа = ОписаниеСтроковогоТипа;
КонецПроцедуры
Шаг 2. Программная обработка начала выбора
Так как поле теперь считается «строковым», стандартная кнопка выбора («...») перестанет открывать форму списка документов. Мы должны переопределить событие НачалоВыбора и открыть нужную форму выбора программно:
&НаКлиенте
Процедура НаименованиеДокументаНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
// Отменяем стандартную обработку выбора платформы
СтандартнаяОбработка = Ложь;
ПараметрыФормы = Новый Структура;
// Если в реквизите уже выбрана ссылка, позиционируем на ней открывающееся окно списка
Если ТипЗнч(Объект.НаименованиеДокумента) <> Тип("Строка") И ЗначениеЗаполнено(Объект.НаименованиеДокумента) Тогда
ПараметрыФормы.Вставить("ТекущаяСтрока", Объект.НаименованиеДокумента);
КонецЕсли;
// Открываем форму выбора конкретного документа (например, АктВыполненияРабот)
ОткрытьФорму("Документ.АктВыполненияРабот.ФормаВыбора", ПараметрыФормы, Элемент);
КонецПроцедуры
Шаг 3. Реализация умного автоподбора при вводе текста
Чтобы система предлагала подходящие документы прямо во время ввода букв с клавиатуры, задействуем обработчик события АвтоПодбор. Мы отключим стандартный поиск платформы и напишем свой легкий запрос к базе данных.
&НаКлиенте
Процедура НаименованиеДокументаАвтоПодбор(Элемент, Текст, ДанныеВыбора, Параметры, Ожидание, СтандартнаяОбработка)
Если Ожидание <> 0 И Не ПустаяСтрока(Текст) Тогда
// Отключаем стандартный поиск платформы
СтандартнаяОбработка = Ложь;
// Получаем список найденных документов с сервера
ДанныеВыбора = ПолучитьСписокДокументовНаСервере(Текст);
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьСписокДокументовНаСервере(Знач ТекстПоиска)
СписокРезультатов = Новый СписокЗначений;
// Выполняем быстрый запрос к нужной таблице документов
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ ПЕРВЫЕ 10
| Акт.Ссылка КАК Ссылка,
| Акт.Представление КАК Представление
|ИЗ
| Документ.АктВыполненияРабот КАК Акт
|ГДЕ
| Акт.Номер ПОДОБНО &ТекстПоиска
| ИЛИ Акт.Контрагент.Наименование ПОДОБНО &ТекстПоиска";
Запрос.УстановитьПараметр("ТекстПоиска", "%" + ТекстПоиска + "%");
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Помещаем в список выбора Ссылку как значение, а текстовое представление — в качестве выводимого имени
СписокРезультатов.Добавить(Выборка.Ссылка, Выборка.Представление);
КонецЦикла;
Возврат СписокРезультатов;
КонецФункции
Результат: Мы получаем максимально отзывчивый интерфейс. Пользователь может беспрепятственно вписать любой произвольный текст «руками». Но если он начнет вводить номер существующего акта, система мгновенно предложит его в выпадающем списке подбора. При выборе из списка в реквизит запишется полноценная ссылка.
Проанализируем ситуацию с точки зрения производительности базы данных и стандартов разработки «1С». Хранение в одном реквизите СУБД типов Строка и ДокументСсылка (или СправочникСсылка) крайне не рекомендуется по следующим причинам:
Для создания правильной и масштабируемой архитектуры рекомендуется использовать один из двух альтернативных вариантов.
Вместо добавления примитивного типа Строка в составной реквизит, создадим легкий служебный справочник (например, ПроизвольныеДокументы) всего с одним стандартным реквизитом Наименование.
В этом случае тип нашего основного реквизита будет составлен исключительно из ссылочных типов: ДокументСсылка.АктВыполненияРабот и СправочникСсылка.ПроизвольныеДокументы.
Если пользователь вводит название документа вручную, система автоматически (в фоновом режиме или при записи документа) создает элемент в справочнике ПроизвольныеДокументы и подставляет ссылку на него в наш реквизит. Это гарантирует, что поле базы данных всегда будет содержать строго ссылочный тип, а индексы СУБД продолжат работать максимально быстро.
В Библиотеке Стандартных Подсистем (БСП) часто используется шаблон разделения данных на уровне реквизитов объекта. Разберем его логику:
ДокументСсылка (только ссылочные типы) и ДокументСтрока (строка фиксированной длины).ИмяДокументаПредставление).Посмотрим на пример кода реализации данного шаблона в модуле формы:
&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
// Заполняем строковое поле отображения на форме при открытии существующего документа
Если ЗначениеЗаполнено(Объект.ДокументСсылка) Тогда
ИмяДокументаПредставление = Строка(Объект.ДокументСсылка);
Иначе
ИмяДокументаПредставление = Объект.ДокументСтрока;
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// Перед записью распределяем данные по реквизитам БД в зависимости от того, что ввел пользователь
Если ПустаяСтрока(ИмяДокументаПредставление) Тогда
ТекущийОбъект.ДокументСсылка = Неопределено;
ТекущийОбъект.ДокументСтрока = "";
Иначе
// Если значение является ссылкой (например, было выбрано интерактивно)
Если ТипЗнч(Объект.ДокументСсылка) <> Тип("Строка") И ЗначениеЗаполнено(Объект.ДокументСсылка) Тогда
ТекущийОбъект.ДокументСсылка = Объект.ДокументСсылка;
ТекущийОбъект.ДокументСтрока = "";
Иначе
// Пользователь ввел произвольный текст вручную
ТекущийОбъект.ДокументСсылка = Неопределено;
ТекущийОбъект.ДокументСтрока = ИмяДокументаПредставление;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Этот подход полностью исключает появление системных окон выбора типа, обеспечивает превосходный UX и гарантирует чистоту структуры базы данных в соответствии со всеми стандартами фирмы «1С».