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