Управляемая форма 1С — окно, которое платформа строит сама по описанию из конфигуратора. Программист задаёт данные, элементы и команды, а тонкий клиент, веб-клиент или мобильное приложение рисуют их каждый по-своему. В 1С 8 управляемые формы появились в версии 8.2 вместе с тонким клиентом, а на платформе 8.3 на них работают все актуальные типовые конфигурации: БП 3.0, УТ 11, КА 2, ERP, УНФ. Ниже — реквизиты, элементы, команды, модуль с директивами компиляции и события: как они связаны и где выполняется код.
Главное отличие от обычной формы: код одной формы исполняется в двух местах — часть на компьютере пользователя, часть на сервере, а между ними ходят только данные формы. Большинство вопросов «почему не работает» сводится к тому, что код оказался не на той стороне.
Структура формы 1С в конфигураторе — несколько вкладок редактора и модуль:
| Часть | Что в ней | Как обратиться из кода |
|---|---|---|
| Реквизиты | Данные: значения, таблицы, основной объект | По имени: Объект.Дата, СтрокаПоиска |
| Элементы | Показ данных: поля, таблицы, группы, кнопки, надписи | Элементы.ИмяЭлемента |
| Команды | Действия, которые выполняются по кнопке или пункту меню | Команды.ИмяКоманды и обработчик в модуле |
| Параметры | Значения, переданные при открытии формы | Параметры.Имя, обычно в ПриСозданииНаСервере |
| Модуль | Обработчики событий и служебные процедуры | Процедуры с директивами &НаКлиенте, &НаСервере и другими |
Связи между частями идут в одну сторону. Элемент ссылается на реквизит через свойство ПутьКДанным, кнопка на команду — через ИмяКоманды, команда и события — на процедуры модуля через палитру свойств. Реквизит об элементах ничего не знает: одно значение можно показать в двух полях или не показывать вовсе и хранить как служебное.
Вот что происходит, когда пользователь нажимает кнопку:
Реквизит управляемой формы хранит всё, что должно пережить переход между клиентом и сервером. Переменная модуля с директивой &НаСервере живёт до конца одного серверного вызова, а реквизит — пока открыта форма.
У формы объекта есть основной реквизит Объект — копия справочника или документа. Это не сам СправочникОбъект, а его данные: записать его напрямую или вызвать метод модуля объекта нельзя. У формы списка основной реквизит называется Список и имеет тип ДинамическийСписок, у формы записи регистра сведений — Запись.
Типы, которых нет на клиенте, платформа в форме подменяет:
| Тип в конфигураторе | Что лежит в форме | Что с ним можно делать |
|---|---|---|
СправочникОбъект, ДокументОбъект | ДанныеФормыСтруктура | Читать и менять реквизиты; табличные части — ДанныеФормыКоллекция |
ТаблицаЗначений | ДанныеФормыКоллекция | Добавить, Вставить, Удалить, Очистить, Количество, НайтиСтроки, Итог, обход в цикле; Свернуть и индексов, как у таблицы значений, нет |
ДеревоЗначений | ДанныеФормыДерево | Строки уровня через ПолучитьЭлементы() |
| Строка, Число, Дата, Булево, ссылки | Как есть | Обычная работа на клиенте и на сервере |
Чтобы на сервере получить настоящий объект и вызвать экспортный метод его модуля, реквизит превращают в значение и обратно:
&НаСервере
Процедура ЗаполнитьНаСервере()
// Из данных формы получаем ДокументОбъект со всеми методами модуля объекта
ДокументОбъект = РеквизитФормыВЗначение("Объект");
ДокументОбъект.ЗаполнитьТабличнуюЧасть(); // экспортная процедура модуля объекта
ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
Модифицированность = Истина;
КонецПроцедуры
Так же работают с таблицей значений: РеквизитФормыВЗначение("Товары") вернёт ТаблицаЗначений со всеми её методами. Обе функции — методы формы и работают только на сервере.
Элементы в управляемых формах 1С отвечают только за показ: поле ввода, флажок, таблица, страницы, надпись, кнопка. Каждый элемент — объект своего типа: ПолеФормы, ГруппаФормы, ТаблицаФормы, ДекорацияФормы, КнопкаФормы. Координат у них нет: вы задаёте порядок и вложенность групп, а размеры и расположение платформа рассчитывает сама под окно и тип клиента.
Реквизит и поле часто называются одинаково, но это разные объекты. СтрокаПоиска = "" меняет значение, Элементы.СтрокаПоиска.Видимость = Ложь прячет поле. Свойства элементов (видимость, доступность, заголовок) меняют и на клиенте, и на сервере. Состав элементов (добавить поле, удалить группу) меняют в серверном коде, обычно в ПриСозданииНаСервере.
У таблицы формы свои события, текущая строка и выделение. ТекущиеДанные и ВыделенныеСтроки есть у элемента Элементы.Товары, а у реквизита Товары их нет. Подробно о колонках и строках — в статье про таблицу формы. Для списков справочников и документов вместо таблицы значений берут динамический список: он читает данные из базы порциями и не держит всю выборку в форме.
Команда — действие без привязки к месту на экране. Её создают на вкладке «Команды», назначают процедуру в свойстве «Действие» и перетаскивают в командную панель или группу — так появляется кнопка. Одну команду можно вывести и кнопкой, и пунктом контекстного меню. Обработчик команды — клиентская процедура с параметром Команда.
Стандартные команды вроде «Записать» и «Провести» платформа добавляет сама, их набор зависит от основного реквизита. Подробнее — в статье о командах формы.
Каждая процедура модуля помечается директивой. Она определяет, где процедура существует и что ей доступно:
| Директива | Где выполняется | Доступ к форме | Для чего |
|---|---|---|---|
&НаКлиенте | На клиенте | Да: реквизиты и элементы | Обработчики команд, ПриИзменении, ПриОткрытии, диалоги |
&НаСервере | На сервере | Да, данные формы передаются на сервер | Заполнение таблиц формы, работа с объектом через РеквизитФормыВЗначение |
&НаСервереБезКонтекста | На сервере | Нет, только параметры | Прочитать из базы значение по ссылке, быстрые проверки |
&НаКлиентеНаСервереБезКонтекста | Там, откуда вызвали | Только через параметр | Общие процедуры: видимость, пересчёт итогов |
Процедура без директивы в модуле формы считается серверной (&НаСервере). В клиентском коде нет запросов, объектов базы данных и менеджеров справочников. Пустую ссылку или предопределённый элемент на клиенте получают через ПредопределенноеЗначение, всё остальное — вызовом сервера.
Вызовы различаются ценой. Контекстный &НаСервере везёт на сервер данные формы, и на документе с большой табличной частью форма начинает подтормаживать на каждом поле. Если серверу нужна только ссылка, чтобы прочитать реквизит из базы, передайте её параметром в процедуру без контекста:
&НаКлиенте
Процедура НоменклатураПриИзменении(Элемент)
Объект.ЕдиницаИзмерения = ЕдиницаНоменклатуры(Объект.Номенклатура);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ЕдиницаНоменклатуры(Номенклатура)
// Функция БСП; имена реквизитов условные — возьмите из своей конфигурации
Возврат ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Номенклатура, "ЕдиницаИзмерения");
КонецФункции
ОбщегоНазначения.ЗначениеРеквизитаОбъекта — серверная функция БСП (версии 2.3 и 3.x), с клиента её напрямую не вызвать. Без БСП здесь нужен короткий запрос.
Код формы запускается из событий. Чаще всего нужны эти:
ПриСозданииНаСервере(Отказ, СтандартнаяОбработка) — главное место настройки: чтение параметров, начальные значения реквизитов, видимость, программное добавление элементов. Выполняется на сервере до показа формы, диалогов здесь нет.ПриЧтенииНаСервере(ТекущийОбъект) — у форм, где основной реквизит читается из базы: формы объекта, записи регистра сведений, набора записей. Для существующих данных срабатывает раньше ПриСозданииНаСервере и повторно по команде «Перечитать».ПриОткрытии(Отказ) — клиентское событие, форма уже создана.ПередЗаписью на клиенте, затем ПередЗаписьюНаСервере, события модуля объекта, ПриЗаписиНаСервере, ПослеЗаписиНаСервере и ПослеЗаписи снова на клиенте.ОбработкаОповещения(ИмяСобытия, Параметр, Источник) — реакция на Оповестить из других форм.Обработчик связывается с событием через палитру свойств, а не по совпадению имени. Процедура ПриОткрытии, вставленная в модуль текстом, не сработает, пока её не выберут в свойствах формы.
Соберём части вместе в форме внешней обработки: пользователь вводит часть наименования, получает список позиций, отмечает нужные и передаёт их в вызвавшую форму. В примере работают все четыре директивы. Запрос обращается к справочнику Номенклатура, который есть в БП, УТ, КА, ERP и УНФ; в другой конфигурации замените таблицу. СтрШаблон и СтрРазделить требуют платформу 8.3.6 или новее и режим совместимости конфигурации не ниже 8.3.6 — в актуальных версиях этих типовых так и есть.
// Модуль формы обработки «Подбор номенклатуры». Управляемая форма, платформа и режим совместимости 8.3.6 и выше.
// Реквизиты формы (создать в редакторе):
// СтрокаПоиска - Строка(100)
// Товары - ТаблицаЗначений, колонки: Выбран (Булево),
// Номенклатура (СправочникСсылка.Номенклатура)
// Элементы: поле СтрокаПоиска, таблица Товары, надпись НадписьИтог,
// кнопка КнопкаПеренести (команда Перенести).
// Команды: НайтиТовары, ОтметитьВсе, Перенести.
// Обработчики событий и команд назначаются в палитре свойств.
#Область ОбработчикиСобытийФормы
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Параметры открытия читаем при создании и сохраняем в реквизит
Если Параметры.Свойство("СтрокаПоиска") Тогда
СтрокаПоиска = Параметры.СтрокаПоиска;
КонецЕсли;
Если Не ПустаяСтрока(СтрокаПоиска) Тогда
НайтиТоварыНаСервере();
КонецЕсли;
УправлениеФормой(ЭтотОбъект);
КонецПроцедуры
#КонецОбласти
#Область ОбработчикиСобытийЭлементовФормы
&НаКлиенте
Процедура СтрокаПоискаПриИзменении(Элемент)
Товары.Очистить();
УправлениеФормой(ЭтотОбъект);
КонецПроцедуры
&НаКлиенте
Процедура ТоварыВыбранПриИзменении(Элемент)
УправлениеФормой(ЭтотОбъект);
КонецПроцедуры
#КонецОбласти
#Область ОбработчикиКомандФормы
&НаКлиенте
Процедура НайтиТовары(Команда)
Если ПустаяСтрока(СтрокаПоиска) Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Введите часть наименования";
Сообщение.Поле = "СтрокаПоиска";
Сообщение.Сообщить();
Возврат;
КонецЕсли;
НайтиТоварыНаСервере();
УправлениеФормой(ЭтотОбъект);
КонецПроцедуры
&НаКлиенте
Процедура ОтметитьВсе(Команда)
// Коллекция формы есть на клиенте: сервер для обхода не нужен
Для Каждого СтрокаТаблицы Из Товары Цикл
СтрокаТаблицы.Выбран = Истина;
КонецЦикла;
УправлениеФормой(ЭтотОбъект);
КонецПроцедуры
&НаКлиенте
Процедура Перенести(Команда)
Выбранные = Новый Массив;
Для Каждого СтрокаТаблицы Из Товары Цикл
Если СтрокаТаблицы.Выбран Тогда
Выбранные.Добавить(СтрокаТаблицы.Номенклатура);
КонецЕсли;
КонецЦикла;
Если Выбранные.Количество() = 0 Тогда
ПоказатьПредупреждение(, "Отметьте хотя бы одну позицию");
Возврат;
КонецЕсли;
// Массив получит обработчик оповещения, переданный в ОткрытьФорму
Закрыть(Выбранные);
КонецПроцедуры
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
&НаСервере
Процедура НайтиТоварыНаСервере()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ ПЕРВЫЕ 200
| ЛОЖЬ КАК Выбран,
| Номенклатура.Ссылка КАК Номенклатура
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа
| И НЕ Номенклатура.ПометкаУдаления
| И Номенклатура.Наименование ПОДОБНО &Шаблон СПЕЦСИМВОЛ ""~""
|
|УПОРЯДОЧИТЬ ПО
| Номенклатура.Наименование";
Запрос.УстановитьПараметр("Шаблон", "%" + ЭкранироватьДляПодобно(СтрокаПоиска) + "%");
// Таблица значений из запроса попадает в реквизит формы (колонки сопоставляются по именам)
ЗначениеВРеквизитФормы(Запрос.Выполнить().Выгрузить(), "Товары");
КонецПроцедуры
&НаСервереБезКонтекста
Функция ЭкранироватьДляПодобно(Знач Текст)
// Сначала сам спецсимвол, потом символы шаблона ПОДОБНО
Результат = СтрЗаменить(СокрЛП(Текст), "~", "~~");
Для Каждого Символ Из СтрРазделить("%,_,[,]", ",") Цикл
Результат = СтрЗаменить(Результат, Символ, "~" + Символ);
КонецЦикла;
Возврат Результат;
КонецФункции
&НаКлиентеНаСервереБезКонтекста
Процедура УправлениеФормой(Форма)
Отмечено = 0;
Для Каждого СтрокаТаблицы Из Форма.Товары Цикл
Если СтрокаТаблицы.Выбран Тогда
Отмечено = Отмечено + 1;
КонецЕсли;
КонецЦикла;
ЭлементыФормы = Форма.Элементы;
ЭлементыФормы.НадписьИтог.Заголовок = СтрШаблон("Показано: %1 (не более 200), отмечено: %2",
Форма.Товары.Количество(), Отмечено);
ЭлементыФормы.КнопкаПеренести.Доступность = (Отмечено > 0);
КонецПроцедуры
#КонецОбласти
ПриСозданииНаСервере сохраняет параметр открытия в реквизит: теперь значение есть и на клиенте, и в следующих вызовах.ОтметитьВсе и СтрокаПоискаПриИзменении обходятся без сервера: реквизит Товары есть на клиенте в виде коллекции, её можно обходить и менять.НайтиТовары проверяет ввод до вызова сервера. Сообщение.Поле с именем реквизита подсвечивает нужное поле формы.НайтиТоварыНаСервере — контекстная: читает реквизит СтрокаПоиска и заполняет Товары через ЗначениеВРеквизитФормы, колонки сопоставляются по именам. РАЗРЕШЕННЫЕ стоит потому, что результат видит пользователь: при ограничении доступа на уровне записей он получит доступные ему позиции, а не ошибку прав.ЭкранироватьДляПодобно работает без контекста: ей хватает строки. Без неё введённые % и _ стали бы символами шаблона.УправлениеФормой вызывается и с сервера, и с клиента. Форма приходит параметром, а сам вызов не пересекает границу клиента и сервера. В надписи сказано «не более 200», потому что запрос ограничен ПЕРВЫЕ 200.Закрыть(Выбранные) возвращает массив в обработчик оповещения вызывающей формы. Как открыть форму с таким обработчиком, описано в статье об открытии формы программно.&НаКлиенте. Перенесите чтение данных в серверную процедуру.&НаСервереБезКонтекста. Передайте значение параметром или смените директиву на &НаСервере.Элементы.Товары.ТекущиеДанные, с проверкой на Неопределено: строка может быть не выбрана.Объект.ЗаполнитьТабличнуюЧасть() — у данных формы нет методов модуля объекта. Нужен РеквизитФормыВЗначение на сервере, как показано выше.СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок и СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку.Обычная форма работает только в толстом клиенте: код и данные в одном месте, элементы расставлены по координатам. Управляемая разделена на клиент и сервер, раскладывается автоматически и работает в тонком, веб- и мобильном клиенте. Обычные формы остались в УТ 10.3, УПП 1.3 и старых самописных базах.
На сервере вызовите РеквизитФормыВЗначение("Товары"), а результат верните через ЗначениеВРеквизитФормы. На клиенте таблицы значений нет, там работают с коллекцией данных формы. Как создать такой реквизит и вывести его на экран, описано в статье о таблице значений на форме.
В реквизите формы — его можно создать без элемента на экране — или в переменной модуля с директивой &НаКлиенте. Серверные переменные модуля между вызовами не сохраняются.
Через расширение: заимствуйте форму, добавьте реквизиты, элементы и обработчики. Обработчики расширения выполняются до, после или вместо исходных, а типовая форма остаётся нетронутой.
Если нужно доработать форму типового документа, добавить подбор или переделать обработку под новую логику, а разбираться с контекстами некогда, отдайте задачу программисту. Посмотрите услуги по доработке 1С или сразу разместите задачу: оценка бесплатная, ответ придёт в течение рабочего дня.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.