При обновлении платформы 1С:Предприятие или снятии режима совместимости разработчики часто сталкиваются с ошибкой «Индекс находится за границами массива» при попытке обратиться к первому элементу отбора динамического списка: Список.Отбор.Элементы[0]. Разберем подробно, почему ранее работавший код перестает функционировать и как правильно управлять отборами динамического списка на современных версиях платформы.
Проанализируем типовой фрагмент кода, вызывающий исключение:
Если ЗначениеЗаполнено(Владелец) Тогда
Список.Отбор.Элементы[0].ПравоеЗначение = Владелец;
Список.Отбор.Элементы[0].Использование = Истина;
Иначе
Список.Отбор.Элементы[0].Использование = Ложь;
КонецЕсли;
В старых версиях платформы (или при включенном режиме совместимости с версией 8.2) отборы, настроенные в конфигураторе, напрямую проецировались в свойство Список.Отбор. В современных версиях 1С:Предприятия 8.3 при снятом режиме совместимости архитектура динамического списка и системы компоновки данных разграничивает слои настроек:
КомпоновщикНастроек.ФиксированныеНастройки): именно к ним обращается свойство Список.Отбор. По умолчанию эта коллекция пуста, если в нее ничего не добавлялось программно, поэтому обращение по индексу [0] вызывает ошибку.КомпоновщикНастроек.ПользовательскиеНастройки): содержат элементы, которые пользователь может интерактивно менять на форме. Эти настройки имеют более высокий приоритет над базовыми настройками и могут перекрывать их.КомпоновщикНастроек.Настройки): именно здесь сохраняются отборы, заданные разработчиком в свойствах динамического списка в конфигураторе.Таким образом, обращение к Список.Отбор.Элементы[0] падает из-за того, что фиксированные настройки не содержат элементов, настроенных в конфигураторе. Кроме того, прямое обращение по жесткому индексу [0] является грубой ошибкой проектирования, так как порядок элементов в коллекции может измениться.
Рассмотрим правильные и надежные способы программной установки отбора.
Если в конфигурации используется «Библиотека стандартных подсистем» (БСП), наиболее правильным решением будет вызов готовой экспортной процедуры. Она самостоятельно выполняет поиск нужного поля, добавляет элемент при его отсутствии и корректно применяет изменения:
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
Список,
"Владелец",
Владелец,
ВидСравненияКомпоновкиДанных.Равно,
"Отбор по владельцу",
ЗначениеЗаполнено(Владелец)
);
Этот метод избавляет от необходимости писать ручные поиски по коллекциям компоновщика настроек и защищает код от ошибок при изменении структуры отборов.
Если отбор должен быть установлен жестко из встроенного языка и пользователь не должен иметь возможности его отключить или изменить, используем работу с фиксированными настройками динамического списка. Напишем универсальный алгоритм поиска и установки элемента:
ПолеОтбора = Новый ПолеКомпоновкиДанных("Владелец");
ЭлементОтбора = Неопределено;
// Выполняем поиск существующего элемента отбора
Для Каждого Элемент Из Список.Отбор.Элементы Цикл
Если Элемент.ЛевоеЗначение = ПолеОтбора Тогда
ЭлементОтбора = Элемент;
Прервать;
КонецЕсли;
КонецЦикла;
// Если элемент не найден — создаем его
Если ЭлементОтбора = Неопределено Тогда
ЭлементОтбора = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = ПолеОтбора;
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
КонецЕсли;
// Задаем значение и активность отбора
Если ЗначениеЗаполнено(Владелец) Тогда
ЭлементОтбора.ПравоеЗначение = Владелец;
ЭлементОтбора.Использование = Истина;
Иначе
ЭлементОтбора.Использование = Ложь;
КонецЕсли;
Если отбор был изначально создан в конфигураторе в составе базовых настроек, доступ к нему осуществляется через КомпоновщикНастроек.Настройки.Отбор. Разберем по шагам, как правильно работать с этим слоем:
ИдентификаторПользовательскойНастройки).Список.КомпоновщикНастроек.ПользовательскиеНастройки, иначе пользовательские настройки перекроют значение базовых.Посмотрим на пример реализации:
ПолеОтбора = Новый ПолеКомпоновкиДанных("Владелец");
ЭлементБазовый = Неопределено;
Для Каждого Элемент Из Список.КомпоновщикНастроек.Настройки.Отбор.Элементы Цикл
Если Элемент.ЛевоеЗначение = ПолеОтбора Тогда
ЭлементБазовый = Элемент;
Прервать;
КонецЕсли;
КонецЦикла;
Если ЭлементБазовый <> Неопределено Тогда
Если Не ПустаяСтрока(ЭлементБазовый.ИдентификаторПользовательскойНастройки) Тогда
// Находим элемент в пользовательских настройках
ЭлементПользовательский = Список.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Найти(
ЭлементБазовый.ИдентификаторПользовательскойНастройки
);
Если ЭлементПользовательский <> Неопределено Тогда
ЭлементПользовательский.ПравоеЗначение = Владелец;
ЭлементПользовательский.Использование = ЗначениеЗаполнено(Владелец);
КонецЕсли;
Иначе
// Меняем в базовых настройках, если элемент не выведен пользователю
ЭлементБазовый.ПравоеЗначение = Владелец;
ЭлементБазовый.Использование = ЗначениеЗаполнено(Владелец);
КонецЕсли;
КонецЕсли;
Если требуется жесткий отбор (например, отображение данных конкретного владельца), зачастую эффективнее использовать не отбор компоновки, а параметр в тексте запроса динамического списка. Для этого в тексте запроса списка указываем условие ГДЕ Владелец = &Владелец и устанавливаем параметр программно:
Список.Параметры.УстановитьЗначениеПараметра("Владелец", Владелец);
Такой подход полностью исключает конфликты пользовательских и программных настроек и обеспечивает оптимальное построение запроса к СУБД.