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