Мы сталкиваемся с распространенной проблемой в разработке на платформе 1С:Предприятие, когда код, успешно работающий в тонком клиенте, демонстрирует некорректное поведение или полностью отказывается функционировать в веб-клиенте. В частности, речь идет об изменении видимости колонок табличной части формы справочника. Пользователь ожидает, что нажатие кнопки будет переключать видимость определенных колонок, но в веб-клиенте эти изменения не сохраняются или сбрасываются. Давайте подробно разберем, почему это происходит и как найти надежное решение.
Чтобы понять корень проблемы, нам необходимо рассмотреть фундаментальные различия между тонким и веб-клиентом в экосистеме 1С:Предприятия.
Тонкий клиент — это полноценное устанавливаемое приложение, которое запускается непосредственно на компьютере пользователя. Он имеет более прямой доступ к ресурсам операционной системы и работает в нативной среде.
Веб-клиент, напротив, исполняется в интернет-браузере. Это означает, что он функционирует в более ограниченной среде, подчиняясь правилам и ограничениям браузера, включая взаимодействие с DOM (Document Object Model) и исполнение JavaScript. Фактически, клиентские модули 1С компилируются в JavaScript для исполнения в браузере.
Несмотря на то что клиентские модули 1С компилируются и исполняются на стороне веб-клиента, функциональность может быть ограничена. Некоторые операции, доступные в тонком клиенте (например, работа с файлами на диске напрямую, специфические системные вызовы), могут быть недоступны или требовать обходных путей в веб-клиенте.
Платформа 1С:Предприятие 8.2 и более поздние версии строго разделяют код на клиентскую и серверную части. Процедуры, помеченные директивой &НаКлиенте, выполняются на стороне клиента и имеют доступ к элементам и реквизитам формы. Однако для доступа к данным базы данных или выполнения сложной бизнес-логики требуется вызывать серверные процедуры, помеченные &НаСервере.
Ключевое отличие заключается в том, что при каждом вызове серверной процедуры из клиентской части весь контекст формы "упаковывается" и передается на сервер, а затем возвращается обратно. Это может быть ресурсоемкой операцией, особенно для форм с большими табличными частями, и замедлять работу в веб-клиенте. Если данные формы не нужны на сервере, более эффективным является использование директивы &НаСервереБезКонтекста, так как в этом случае контекст не передается.
Именно динамический характер взаимодействия веб-клиента с сервером для получения данных и обновления интерфейса часто приводит к тому, что клиентские изменения видимости колонок могут быть сброшены или переопределены в процессе этого взаимодействия, особенно после серверных вызовов или других событий, которые заставляют форму "перерисовываться" или обновлять свое состояние.
Изначально, разработчики часто пытаются решить проблему видимости колонок, просто изменив свойство Видимость элементов формы в клиентском обработчике команды. Например:
&НаКлиенте
Процедура КомандаПоказатьСкрытьКолонки(Команда)
Элементы.ТабличнаяЧасть.Колонки.МояКолонка1.Видимость = Истина;
Элементы.ТабличнаяЧасть.Колонки.МояКолонка2.Видимость = Ложь;
КонецПроцедуры
В тонком клиенте такой подход может работать. Однако в веб-клиенте эти изменения часто сбрасываются. Первой логичной догадкой является то, что веб-клиенту для сохранения состояния нужны серверные вызовы. Мы можем попробовать перенести логику в серверную процедуру:
&НаКлиенте
Процедура КомандаПоказатьСкрытьКолонки(Команда)
ПоказатьСкрытьКолонкиНаСервере();
КонецПроцедуры
&НаСервере
Процедура ПоказатьСкрытьКолонкиНаСервере()
// Здесь мы пытаемся изменить видимость колонок
// Это не совсем корректный подход, так как видимость колонок
// это клиентское свойство элемента формы
// Правильнее было бы передать данные на сервер,
// обработать их, а затем вернуть на клиент
// и там уже изменить видимость
КонецПроцедуры
Как показывает обсуждение на форуме, даже вызов серверной процедуры не всегда решает проблему. Почему? Потому что изменение видимости колонок — это, по своей сути, клиентская операция. Сервер не управляет видимостью элементов интерфейса напрямую. Он может повлиять на данные, на основе которых клиент потом строит интерфейс, но непосредственное управление Видимость происходит на клиенте.
Ключевой момент, который был выявлен в ходе обсуждения (сообщение 8), — это влияние обработчика ПриАктивизацииСтроки. Оказывается, после выполнения нашей команды, веб-клиент или какая-либо логика формы вызывает обработчик ПриАктивизацииСтроки, который, в свою очередь, сбрасывает видимость нужных колонок обратно в Ложь. Это происходит потому, что ПриАктивизацииСтроки часто используется для динамического управления интерфейсом в зависимости от данных текущей строки, и он может содержать логику, устанавливающую видимость колонок по умолчанию или в соответствии с определенными условиями.
В веб-клиенте, из-за постоянной синхронизации состояния формы с сервером и особенностей перерисовки интерфейса в браузере, такие обработчики, как ПриАктивизацииСтроки, могут срабатывать более агрессивно или в неожиданные моменты, переопределяя наши пользовательские изменения.
Для надежного управления видимостью колонок в веб-клиенте, особенно в условиях, когда другие обработчики могут вмешиваться, нам необходимо ввести явное хранение состояния. Мы будем использовать дополнительный реквизит формы, который будет служить флагом, указывающим текущий режим отображения колонок. Этот флаг будет контролировать, должны ли колонки быть видны или скрыты, а также подавлять нежелательное поведение обработчика ПриАктивизацииСтроки.
Мы добавляем новый реквизит формы, например, ПоказыватьВсеПоказатели, типа Булево. Этот реквизит будет хранить текущее состояние: Истина, если нужно показывать все показатели (колонки), и Ложь, если нужно скрывать некоторые.
Команда, привязанная к кнопке, теперь будет не просто менять видимость, а переключать значение нашего нового реквизита ПоказыватьВсеПоказатели. После этого, в зависимости от нового значения реквизита, мы будем устанавливать видимость колонок и, возможно, отключать/включать отбор данных. Также крайне удобно использовать свойство кнопки Пометка, чтобы визуально информировать пользователя о текущем режиме (кнопка "нажата" или "отжата").
Примерная логика команды будет выглядеть так:
&НаКлиенте
Процедура КомандаПоказатьСкрытьКолонки(Команда)
// Переключаем значение нашего флага
ПоказыватьВсеПоказатели = НЕ ПоказыватьВсеПоказатели;
Если ПоказыватьВсеПоказатели Тогда
// Режим "Показывать все показатели" активирован
Элементы.ТабличнаяЧасть.Колонки.МояКолонка1.Видимость = Истина;
Элементы.ТабличнаяЧасть.Колонки.МояКолонка2.Видимость = Истина;
// Отключаем отбор, если он был
// ОтборПоказателей = Неопределено;
Элементы.КомандаПоказатьСкрытьКолонки.Пометка = Истина; // Показываем, что кнопка "нажата"
Иначе
// Режим "Скрыть некоторые показатели" активирован
Элементы.ТабличнаяЧасть.Колонки.МояКолонка1.Видимость = Истина; // Пример: эту колонку всегда видно
Элементы.ТабличнаяЧасть.Колонки.МояКолонка2.Видимость = Ложь; // Эту колонку скрываем
// Включаем отбор, если нужно
// УстановитьОтборПоказателей();
Элементы.КомандаПоказатьСкрытьКолонки.Пометка = Ложь; // Показываем, что кнопка "отжата"
КонецЕсли;
// Если есть серверная логика, которая также зависит от этого состояния,
// можно вызвать серверную процедуру для синхронизации, передав ей состояние флага.
// Например:
// ОбновитьДанныеНаСервере(ПоказыватьВсеПоказатели);
КонецПроцедуры
ПриАктивизацииСтроки:
Теперь самое главное: мы должны "обезвредить" обработчик ПриАктивизацииСтроки, чтобы он не сбрасывал наши настройки видимости. Мы добавим в него заглушку, которая будет проверять значение нашего флага ПоказыватьВсеПоказатели.
Пример кода:
&НаКлиенте
Процедура ТабличнаяЧастьПриАктивизацииСтроки(Элемент)
// Если наш флаг установлен в Истина, это означает,
// что пользователь хочет видеть все показатели,
// и мы не должны вмешиваться в видимость колонок
Если ПоказыватьВсеПоказатели Тогда
Возврат; // Выходим из процедуры, не выполняя стандартную логику скрытия
КонецЕсли;
// Здесь располагается стандартная логика обработчика ПриАктивизацииСтроки,
// которая могла бы скрывать колонки
// Например:
// Если Элемент.ТекущиеДанные.ТипСтроки = Перечисления.ТипыСтрокТаблицы.Итого Тогда
// Элементы.ТабличнаяЧасть.Колонки.МояКолонка2.Видимость = Ложь;
// Иначе
// Элементы.ТабличнаяЧасть.Колонки.МояКолонка2.Видимость = Истина;
// КонецЕсли;
КонецПроцедуры
Таким образом, когда режим "Показывать все показатели" активен, ПриАктивизацииСтроки не будет выполнять свою логику, которая могла бы скрывать колонки.
Как упоминается в сообщении 11, иногда свойство (или состояние) нужно изменить и на клиенте, и одновременно в серверном вызове. Если ваша серверная логика также зависит от режима отображения колонок (например, для формирования отчета или выполнения расчетов), то после изменения флага на клиенте, вам нужно будет вызвать серверную процедуру и передать ей текущее значение флага:
&НаКлиенте
Процедура КомандаПоказатьСкрытьКолонки(Команда)
// ... (код переключения флага и установки видимости на клиенте) ...
СообщитьСостояниеФлагаНаСервер(ПоказыватьВсеПоказатели);
КонецПроцедуры
&НаСервереБезКонтекста // Или &НаСервере, если нужен контекст формы
Процедура СообщитьСостояниеФлагаНаСервер(СостояниеФлага)
// На сервере можно использовать это состояние для своей логики
Если СостояниеФлага Тогда
// ... серверная логика для режима "все показатели" ...
Иначе
// ... серверная логика для режима "скрытые показатели" ...
КонецЕсли;
КонецПроцедуры
Это гарантирует, что и клиентская, и серверная часть приложения будут работать с согласованным представлением о текущем режиме отображения данных.
При работе с формами в веб-клиенте, особенно с табличными частями, мы можем применить несколько дополнительных оптимизаций и советов:
Как было предложено в сообщении 9, вместо кнопки вы можете вывести сам реквизит формы ПоказыватьВсеПоказатели на форму в виде флага (флажка). При изменении значения этого флажка вы будете вызывать ту же логику, что и в обработчике команды. Это может быть более интуитивно для пользователя.
Для форм, где пользователь активно редактирует табличные документы, рекомендуется выполнять все перерасчеты состояния формы (установку значений соседних ячеек, доступность элементов управления) на клиенте. Это помогает избежать лишних обращений на сервер и сохраняет приемлемую скорость работы. Если серверный вызов все же необходим, убедитесь, что он выполняется только один раз и по возможности используйте беcконтекстные вызовы (&НаСервереБезКонтекста), чтобы минимизировать объем передаваемых данных.
Большое количество колонок, объединений ячеек и сложная структура макетов табличных документов также может влиять на время открытия формы и скорость работы в веб-клиенте. Оптимизация макетов и уменьшение их сложности, когда это возможно, может улучшить производительность.
Проблема с видимостью колонок в веб-клиенте 1С, особенно когда изменения сбрасываются обработчиками вроде ПриАктивизацииСтроки, является классическим примером особенностей клиент-серверного взаимодействия и динамического обновления интерфейса в веб-среде. Мы выяснили, что прямое изменение видимости на клиенте часто недостаточно, а простой серверный вызов не решает проблему полностью, так как видимость управляется на клиенте. Корректное решение заключается в создании и управлении явным состоянием (булевым реквизитом формы), которое не только переключает видимость, но и подавляет нежелательное поведение других обработчиков формы. Применяя этот подход и учитывая особенности веб-клиента, мы можем создавать надежные и функциональные решения в 1С:Предприятии.