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