Расширенный автоподбор при вводе по строке в поле ввода в управляемых формах

21.01.21

Разработка - Механизмы платформы 1С

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

Ниже будет выложен готовый код. А тут несколько вводных слов к нему.

Альтернативный алгоритм максимально близок к штатному автоподбору при вводе по строке с режимом поиска по строке "Любая часть". Строки результата в которых слова поиска находятся в начале строки выдаются первыми. Чтобы изменить количество строк выдаваемого результата достаточно изменить значение переменной "МаксимумСтрокРезультата". Из забавного: подсветку слов поиска в результате пришлось реализовывать вручную в функции ПодсветитьСлова() и это оказалось сопоставимо по объему с остальным кодом.

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

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

&НаКлиенте
Процедура ТоварыНоменклатураАвтоПодбор(Элемент, Текст, ДанныеВыбора, ПараметрыПолученияДанных, Ожидание, СтандартнаяОбработка)
	
	СтандартнаяОбработка = Ложь;
	ДанныеВыбора = ПолучитьДанныеВыбораРасширенные(Текст);
	
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьДанныеВыбораРасширенные(СтрокаПоиска)
	
	Возврат Справочники.Номенклатура.ПолучитьДанныеВыбора(Новый Структура("СтрокаПоиска, ВводПоСтрокеРасширенный", СтрокаПоиска, Истина));
	
КонецФункции

Как видно из кода выше, для получения данных выбора мы передаем структуру с двумя параметрами - "СтрокаПоиска" и "ВводПоСтрокеРасширенный". Первый параметр является предопределенным и обязательным. А второй - это наш опциональный признак использования альтернативного алгоритма.

Ниже код модуля менеджера справочника "Номенклатура":

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

Функция ПодсветитьСлова(ИсходнаяСтрока, МассивСловПодсветки, Знач ШрифтПодсветки = Неопределено, Знач ЦветПодсветки = Неопределено)
	
	МассивРезультирующихСтрок = Новый Массив;
	
	Если ШрифтПодсветки = Неопределено Тогда
		ШрифтПодсветки = Новый Шрифт(,, Истина); // жирный
	КонецЕсли;
	
	Если ЦветПодсветки = Неопределено Тогда
		ЦветПодсветки = Новый Цвет(0, 153, 0); // светло-зеленый из стандартной подсветки ввода по строке
	КонецЕсли;
	
	ИсходнаяСтрокаНормализованная = НРег(ИсходнаяСтрока); // нормализация
	ДлинаИсходнойСтроки = СтрДлина(ИсходнаяСтрокаНормализованная);
	
	// получим список диапазонов символов для подсветки
	СписокДиапазонов = Новый СписокЗначений;
	Для НомерСлова = 1 По МассивСловПодсветки.Количество() Цикл
		СловоПодсветки = НРег(МассивСловПодсветки[НомерСлова - 1]); // нормализация
		ПозицияНачалаСлова = 1;
		ДлинаСлова = СтрДлина(СловоПодсветки);
		Пока ПозицияНачалаСлова <> 0 И ПозицияНачалаСлова <= ДлинаИсходнойСтроки Цикл
			ПозицияНачалаСлова = СтрНайти(ИсходнаяСтрокаНормализованная, СловоПодсветки, , ПозицияНачалаСлова);
			Если ПозицияНачалаСлова > 0 Тогда
				МассивДиапазона = Новый Массив;
				МассивДиапазона.Добавить(ПозицияНачалаСлова);
				МассивДиапазона.Добавить(ПозицияНачалаСлова + ДлинаСлова - 1);
				СписокДиапазонов.Добавить(МассивДиапазона, Формат(ПозицияНачалаСлова, "ЧЦ=3; ЧВН="));
				ПозицияНачалаСлова = ПозицияНачалаСлова + ДлинаСлова;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	// отсортируем список диапазонов по возрастанию начал диапазонов
	СписокДиапазонов.СортироватьПоПредставлению();
	
	// сформируем массив результирующих строк с разным оформлением (подсветкой)
	СтартоваяПозиция = 1;
	Для Каждого ЭлементДиапазона Из СписокДиапазонов Цикл
		НачалоДиапазона = ЭлементДиапазона.Значение[0];
		КонецДиапазона = ЭлементДиапазона.Значение[1];
		Если НачалоДиапазона > СтартоваяПозиция Тогда // часть строки без подсветки
			ЧастьБезВыделения = Сред(ИсходнаяСтрока, СтартоваяПозиция, НачалоДиапазона - СтартоваяПозиция);
			МассивРезультирующихСтрок.Добавить(Новый ФорматированнаяСтрока(ЧастьБезВыделения));
			СтартоваяПозиция = НачалоДиапазона;
		КонецЕсли;
		Если КонецДиапазона >= СтартоваяПозиция Тогда // часть строки с подсветкой
			ЧастьВыделяемая = Сред(ИсходнаяСтрока, СтартоваяПозиция, КонецДиапазона - СтартоваяПозиция + 1);
			МассивРезультирующихСтрок.Добавить(Новый ФорматированнаяСтрока(ЧастьВыделяемая, ШрифтПодсветки, ЦветПодсветки));
			СтартоваяПозиция = КонецДиапазона + 1;
		КонецЕсли;
	КонецЦикла;
	
	Если СтартоваяПозиция <= ДлинаИсходнойСтроки Тогда // "хвост" без выделения
		Хвост = Сред(ИсходнаяСтрока, СтартоваяПозиция, ДлинаИсходнойСтроки - СтартоваяПозиция + 1);
		МассивРезультирующихСтрок.Добавить(Новый ФорматированнаяСтрока(Хвост));
	КонецЕсли;
	
	Возврат Новый ФорматированнаяСтрока(МассивРезультирующихСтрок);
	
КонецФункции

Вот, собственно, и все чем я хотел поделиться в этой статье. Надеюсь, кому-то она сэкономит время.

автоподбор переопределение ввод по строке УФ управляемые формы обработкаполученияданныхвыбора

См. также

Сервисы интеграции без Шины и интеграции

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Пример использования «Сервисов интеграции» без подключения к Шине и без обменов.

13.03.2024    2665    dsdred    16    

59

Поинтегрируем: сервисы интеграции – новый стандарт или просто коннектор?

Обмен между базами 1C Администрирование СУБД Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

В платформе 8.3.17 появился замечательный механизм «Сервисы интеграции». Многие считают, что это просто коннектор 1С:Шины. Так ли это?

11.03.2024    6169    dsdred    59    

86

Как готовить и есть массивы

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

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

24.01.2024    6049    YA_418728146    25    

68

Планы обмена VS История данных

Обмен между базами 1C Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Вы все еще регистрируете изменения только на Планах обмена и Регистрах сведений?

11.12.2023    7134    dsdred    36    

114

1С-ная магия

Механизмы платформы 1С Бесплатно (free)

Язык программирования 1С содержит много нюансов и особенностей, которые могут приводить к неожиданным для разработчика результатам. Сталкиваясь с ними, программист начинает лучше понимать логику платформы, а значит, быстрее выявлять ошибки и видеть потенциальные узкие места своего кода там, где позже можно было бы ещё долго медитировать с отладчиком в поисках источника проблемы. Мы рассмотрим разные примеры поведения кода 1С. Разберём результаты выполнения и ответим на вопросы «Почему?», «Как же так?» и «Зачем нам это знать?». 

06.10.2023    19238    SeiOkami    46    

119

Дефрагментация и реиндексация после перехода на платформу 8.3.22

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Начиная с версии платформы 8.3.22 1С снимает стандартные блокировки БД на уровне страниц. Делаем рабочий скрипт, как раньше.

14.09.2023    12962    human_new    27    

76

Валидация JSON через XDTO (включая массивы)

WEB-интеграция Универсальные функции Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    9558    YA_418728146    6    

143

Внешние компоненты Native API на языке Rust - Просто!

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

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

20.08.2023    6577    sebekerga    54    

95
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. Evg-Lylyk 4622 21.01.21 08:57 Сейчас в теме
Хороший пример работы с этим механизмом https://infostart.ru/public/89892/
2. herfis 499 21.01.21 12:08 Сейчас в теме
(1) Судя по описанию - довольно круто. Я на универсальную подсистему не замахивался :) Чисто точечное решение. Ну и в силу компактности - удобное в качестве знакомства с механизмом, поэтому и опубликовал.
ЗЫ. Кстати, а подсветка слов поиска в строках результата в подсистеме есть?) Если нет - у меня пример реализации. Правда, чтобы не усложнять алгоритм у меня только первое вхождение подсвечивается.
3. herfis 499 21.01.21 12:37 Сейчас в теме
Допилил функцию ПодсветитьСлова() чтобы подсвечивались все вхождения слов, а не только первые.
4. Evg-Lylyk 4622 21.01.21 14:18 Сейчас в теме
(2) По выделению с 15 платформы есть функция СтрНайтиИВыделитьОформлением
может подойдет
5. herfis 499 21.01.21 14:27 Сейчас в теме
(4)
СтрНайтиИВыделитьОформлением

Черт, вероятно я опять велосипед изобрел :)
Просто до сих пор на 12 релизе сижу.
9. Matematik 10.01.23 14:26 Сейчас в теме
(5)
СтрНайтиИВыделитьОформлением

Эта функция ищет только по началу слова.
6. caponid 21.01.21 17:59 Сейчас в теме
В качестве разминки для ума у меня получилось как то так, с рекурсией написалось ))
Функция ПодсветитьСлова(ИсходнаяСтрока, МассивСловПодсветки, Знач ШрифтПодсветки = Неопределено, Знач ЦветПодсветки = Неопределено)
	Если ШрифтПодсветки = Неопределено Тогда
		ШрифтПодсветки = Новый Шрифт(,, Истина); // жирный
	КонецЕсли;
	
	Если ЦветПодсветки = Неопределено Тогда
		ЦветПодсветки = Новый Цвет(0, 153, 0); // светло-зеленый из стандартной подсветки ввода по строке
	КонецЕсли;
	
	ПП = Новый Структура("Цвет, Шрифт", ЦветПодсветки, ШрифтПодсветки); // ПараметрыПодсветки
	Подсветка = РазбратьСтрокуДляПодсветки(ВРЕГ(ИсходнаяСтрока), МассивСловПодсветки);
	МассивСтрок = СобратьСтрокуСПодсветкой(ИсходнаяСтрока, Подсветка, ПП);
	
	Возврат Новый ФорматированнаяСтрока(МассивСтрок);
КонецФункции

Функция РазбратьСтрокуДляПодсветки(знач ВРСтрока, МассивСлов, Индекс = 0)
	Слово = МассивСлов[Индекс];
	ВРСлово = ВРЕГ(Слово);
	Если СтрНайти(ВРСтрока, ВРСлово) = 0 Тогда Возврат ВРСтрока; КонецЕсли; 
	СимволЗамены = Символ(1);
	Сегменты = СтрРазделить(СтрЗаменить(ВРСтрока, ВРСлово, СимволЗамены), СимволЗамены);
	Если Индекс < МассивСлов.ВГраница() Тогда
		Индекс = Индекс + 1;
		Для сч = 0 По Сегменты.ВГраница() Цикл
			Сегменты[сч] = РазбратьСтрокуДляПодсветки(Сегменты[сч], МассивСлов, Индекс);
		КонецЦикла;
	КонецЕсли; 
	Возврат Новый Структура("Слово, Сегменты", Слово, Сегменты);
КонецФункции
 
Функция СобратьСтрокуСПодсветкой(ИсходнаяСтрока, Подсветка, ПП, МассивСтрок = Неопределено, Позиция = 1)
	Если МассивСтрок = Неопределено Тогда МассивСтрок = Новый Массив КонецЕсли; 
	Граница = Подсветка.Сегменты.ВГраница();
	Для сч = 0 По Граница Цикл
		Сегмент = Подсветка.Сегменты[сч];
		Если типЗнч(Сегмент) = Тип("Структура") Тогда
			СобратьСтрокуСПодсветкой(ИсходнаяСтрока, Сегмент, ПП, МассивСтрок, Позиция);
		Иначе	
			Длина = СтрДлина(Сегмент);
			МассивСтрок.Добавить(Сред(ИсходнаяСтрока, Позиция, Длина));
			Позиция = Позиция + Длина;
		КонецЕсли;
		Если сч = Граница Тогда Прервать КонецЕсли;
		Длина = СтрДлина(Подсветка.Слово);
		МассивСтрок.Добавить(Новый ФорматированнаяСтрока(Сред(ИсходнаяСтрока, Позиция, Длина), ПП.Шрифт, ПП.Цвет));
		Позиция = Позиция + Длина;
	КонецЦикла; 
	Возврат МассивСтрок; 
КонецФункции
Показать
7. deminded 7 26.01.21 12:46 Сейчас в теме
А никому не попадалось решение, чтобы можно было подбирать в списке выбора для текстового поля отдельные слова?
Т.е. чтобы при выборе фокус не выходил из редактирования поля, а вставлял выбранное слово в уже набранный текст, и можно было продолжить ввод...
8. maksa2005 534 07.12.22 10:08 Сейчас в теме
10. baracuda 2 19.06.23 13:04 Сейчас в теме
Спасибо за ваше решение.
Я правда немного другую задачу решал.
Пытался ограничить список выбора, по нужным мне значениям.
По итогу когда я программно заполняю ДанныеВыбора мои отобранные значения показываются первыми без подстветки, данные по по иску отображаются дальше но уже с подсветкой строк.
Не могу понять как из отсеять вообще.
11. ivan_vostok@mail.ru 04.10.23 12:28 Сейчас в теме
Спасибо за статью, от души!
Код 100% рабочий.
Упростил жизнь, без лишних велосипедов.
12. DebilniySaytInfostat 22.01.24 10:57 Сейчас в теме
Я не много глуповат. Подскажите пожалуйста. У автора написано
К слову, получение данных выбора можно инициировать программно, вызвав метод модуля менеджера объекта ПолучитьДанныеВыбора()
а в описании все равно по коду вызывается Процедура ОбработкаПолученияДанныхВыбора(ДанныеВыбора, Параметры, СтандартнаяОбработка)
Оставьте свое сообщение