В практике разработки на платформе 1С:Предприятие 8.3 часто возникает задача создания интерфейса типа "Мастер-Деталь" или "Дашборд". В таких случаях на одной форме располагается список документов или справочников, а рядом — блок для быстрого просмотра и редактирования выбранного элемента. Основная сложность заключается в том, что в управляемом приложении работа с типами ДокументОбъект или СправочникОбъект возможна только на сервере, а передать их "как есть" на клиент нельзя. Разберем детально, как правильно реализовать получение объекта по ссылке и его отображение в блоке редактирования.
Рассмотрим ситуацию, с которой столкнулся автор темы. Имеется таблица со ссылками на документы, и при активации строки необходимо загрузить данные этого документа в специальный реквизит формы для редактирования. Попытка вызвать метод ПолучитьОбъект() на клиенте предсказуемо завершается ошибкой, так как этот метод доступен только на сервере. Однако и простая передача объекта с сервера на клиент через функцию также невозможна.
Проанализируем типичную ошибку начинающего разработчика:
&НаСервере
Функция _ПолучитьОбъект(_Ссылка)
Возврат _Ссылка.ПолучитьОбъект(); // Объект нельзя вернуть на клиент!
КонецФункции
При выполнении этого кода система выдаст сообщение: "Ошибка передачи данных между клиентом и сервером. Значение недопустимого типа". Это происходит потому, что объект данных (например, ДокументОбъект.РеализацияТоваровУслуг) не является сериализуемым для передачи между программными слоями. Он предназначен для выполнения серверных операций (запись, проведение, проверка заполнения) и не может существовать в памяти клиентского приложения.
Для того чтобы данные объекта стали доступны на форме для редактирования, необходимо использовать механизм соответствия между объектом базы данных и реквизитом управляемой формы. Основным инструментом здесь выступает метод ЗначениеВРеквизитФормы.
Разберем по шагам, как настроить форму:
ДокументРедактирования). В колонке "Тип" выберем тип нужного документа, но укажем не ссылку, а именно ДокументОбъект.Рассмотрим правильный программный код для реализации этой задачи:
&НаСервере
Процедура ЗагрузитьОбъектНаФорму(ВыбраннаяСсылка)
// Получаем объект из ссылки
ОбъектБД = ВыбраннаяСсылка.ПолучитьОбъект();
// Копируем данные из объекта в реквизит формы
// "ДокументРедактирования" — это имя реквизита формы с типом ДокументОбъект
ЗначениеВРеквизитФормы(ОбъектБД, "ДокументРедактирования");
КонецПроцедуры
Этот метод работает эффективно, так как платформа сама берет на себя задачу по "распаковке" данных объекта в структуру реквизитов формы, которые уже доступны на клиенте для отображения и ввода пользователем.
После того как пользователь отредактировал данные в блоке, их необходимо сохранить. Процесс обратный: нам нужно преобразовать данные из реквизита формы в объект, а затем вызвать метод Записать(). Проанализируем пример реализации сохранения:
&НаСервере
Процедура СохранитьИзмененияНаСервере()
// Преобразуем данные формы обратно в объект данных
ОбъектДляЗаписи = РеквизитФормыВЗначение("ДокументРедактирования");
// Теперь у нас есть полноценный объект, который можно записать
Попытка
ОбъектДляЗаписи.Записать(РежимЗаписиДокумента.Запись);
// После записи полезно обновить данные на форме,
// чтобы актуализировать версию данных или номер
ЗначениеВРеквизитФормы(ОбъектДляЗаписи, "ДокументРедактирования");
Исключение
Сообщить("Ошибка при сохранении: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Если вы вызываете загрузку объекта в событии ПриАктивацииСтроки, важно помнить о производительности. При быстром пролистывании списка пользователем сервер будет перегружен запросами на получение объектов. Чтобы избежать "тормозов" интерфейса, рекомендуется использовать следующий прием:
Проанализируем алгоритм с использованием обработчика ожидания:
&НаКлиенте
Процедура СписокПриАктивацииСтроки(Элемент)
// Отключаем предыдущий вызов, если пользователь нажал кнопку быстро
ОтключитьОбработчикОжидания("ИсполнитьЗагрузкуОбъекта");
// Запускаем загрузку с задержкой в 0.3 - 0.5 секунды
ПодключитьОбработчикОжидания("ИсполнитьЗагрузкуОбъекта", 0.3, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ИсполнитьЗагрузкуОбъекта()
ТекущиеДанные = Элементы.Список.ТекущиеДанные;
Если ТекущиеДанные <> Неопределено Тогда
ЗагрузитьОбъектНаФорму(ТекущиеДанные.Ссылка);
КонецЕсли;
КонецПроцедуры
Такой подход делает интерфейс отзывчивым, так как серверные вызовы происходят только тогда, когда пользователь остановился на конкретной строке.
Иногда нет смысла загружать весь ДокументОбъект (со всеми его табличными частями и десятками реквизитов), если в блоке редактирования нужно поменять только пару полей (например, "Статус" или "Комментарий"). В этом случае выгоднее создать на форме простые реквизиты (строковые, числовые) и заполнять их напрямую из ссылки.
Рассмотрим пример частичного обновления:
&НаСервере
Процедура ЗагрузитьТолькоНужныеПоля(Ссылка)
// Читаем данные напрямую, не получая тяжелый объект
Данные = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Ссылка, "Статус, Комментарий");
ЭлементСтатус = Данные.Статус;
ЭлементКомментарий = Данные.Комментарий;
КонецПроцедуры
&НаСервере
Процедура СохранитьТолькоНужныеПоля(Ссылка)
Объект = Ссылка.ПолучитьОбъект();
Объект.Статус = ЭлементСтатус;
Объект.Комментарий = ЭлементКомментарий;
Объект.Записать();
КонецПроцедуры
Важно: При использовании метода ЗначениеВРеквизитФормы платформа автоматически отслеживает Модифицированность формы. Если вы меняете данные в блоке редактирования, форма "поймет", что она изменена, и предложит сохранить данные при закрытии.
Если ваша задача подразумевает и создание новых документов в этом же интерфейсе, то на сервере следует использовать метод СоздатьДокумент(). Выясним, как это выглядит в коде:
&НаСервере
Процедура СоздатьНовыйВБлоке()
НовыйОбъект = Документы.ВашТипДокумента.СоздатьДокумент();
// Заполняем значения по умолчанию, если нужно
НовыйОбъект.Дата = ТекущаяДата();
// Отображаем на форме
ЗначениеВРеквизитФормы(НовыйОбъект, "ДокументРедактирования");
КонецПроцедуры
В этом случае в реквизите ДокументРедактирования окажется объект с пустой ссылкой. При сохранении (через РеквизитФормыВЗначение и Записать) ссылка будет создана, и вы сможете обновить список (таблицу) на основной форме, чтобы новый документ там появился.
Подводя итог, можно сказать, что работа с объектами по ссылке в управляемых формах требует четкого понимания разделения между данными базы (объектами) и данными интерфейса (реквизитами формы). Использование ЗначениеВРеквизитФормы является наиболее универсальным и быстрым способом реализовать "вложенное" редактирование без открытия лишних окон.