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