1C-CodeОсновы 1С → Формы: реквизиты, элементы, события

Формы: реквизиты, элементы, события

То, что человек видит и трогает. Реквизит формы против реквизита объекта, порядок событий при открытии и записи, и почему поле «есть в объекте, но нет на экране».

Глава 23 из 36 · чтение примерно 32 минут

Форма — единственная часть программы, которую видит пользователь. Всё остальное он оценивает по ней.

И это же место, где начинающий разработчик проводит больше всего времени в недоумении: добавил реквизит — на экране ничего; поменял значение — не сохранилось; написал обработчик — не вызывается.

Два разных набора данных

Главное различие главы. У формы есть свои реквизиты, и они не то же самое, что реквизиты объекта.

Реквизит объекта — поле документа или справочника, хранится в базе. Живёт в конфигураторе у самого объекта.

Реквизит формы — переменная формы. Существует, пока форма открыта, в базу не попадает никогда. Живёт в конфигураторе у формы.

Форма документа устроена так: у неё есть один реквизит по имени Объект, и в нём лежит копия документа. Плюс сколько угодно своих — для того, что нужно только на экране: выбранный склад в подборе, флажок «показывать остатки», итог по выделенным строкам.

Почему «добавил реквизит, а его нет»

Реквизит объекта и элемент формы — разные сущности, и одно не появляется из другого само.

Добавили реквизит документу — он есть в базе, но форма о нём не знает: её состав задан отдельно. Нужно открыть форму и перетащить реквизит в дерево элементов.

Обратное тоже верно: удалили реквизит объекта, а элемент формы остался — форма перестанет открываться с ошибкой «поле не найдено».

Элементы формы

Элемент — это то, что нарисовано: поле ввода, кнопка, таблица, группа, декорация. У элемента есть ПутьКДанным — указание, какой реквизит он показывает.

Один реквизит может быть показан несколькими элементами; элемент может не показывать ничего (кнопка, надпись). Поэтому в коде обращаются по-разному: к данным через Объект.Склад, к внешнему виду через Элементы.Склад.

Свойства элемента — Видимость, Доступность, ТолькоПросмотр, Заголовок — меняются кодом на клиенте: это внешний вид, а не данные.

Данные и вид — разные обращения

Объект.Склад = ВыбранныйСклад;
Элементы.Склад.Доступность = Ложь;
Элементы.Скидка.Видимость = Объект.ЕстьСкидка;
ЧастьЧто означает
Объект.Складданные: попадёт в базу при записи
Элементы.Складвнешний вид: в базу не попадает никогда
Доступность = Ложьполе видно, но изменить нельзя
Видимость = …поля вообще нет на экране; данные при этом остаются

Спрятать — не значит очистить

Классическая ошибка отчётов: поле спрятали, а значение в нём осталось — и отбор продолжает работать по невидимому полю. Человек видит не тот результат и не понимает почему.

Правило: спрятали поле — очистите данные. Видимость и значение меняют вместе, а не по отдельности.

Порядок событий при открытии

Форма открывается не одним шагом, и знать порядок нужно, чтобы понимать, почему «в этом месте данных ещё нет».

ПриСозданииНаСервере — форма собирается. Есть база, нет человека. Здесь заполняют свои реквизиты и настраивают элементы.

ПриОткрытии — на клиенте, форма уже собрана и показывается. Здесь можно спрашивать и показывать.

При записи порядок такой: ПередЗаписью (клиент) → ПередЗаписьюНаСервере → запись объекта → ПослеЗаписиНаСервереПослеЗаписи (клиент).

Отказаться от записи можно в любом из «перед»: параметр Отказ = Истина.

Порядок событий при записи

Открытие — половина дела. Вторая половина, и та, где ошибаются дороже, — запись. Нажатие «Записать и закрыть» разворачивается в цепочку из восьми шагов, и они идут по очереди на разных сторонах.

Важно здесь одно: часть шагов живёт в модуле формы, часть — в модуле объекта. Форма записывает не себя, а объект, и объект о форме ничего не знает.

Что за чем выполняется при записи

ШагГдеЧто там уместно
ПередЗаписьюформа, клиентспросить человека; Отказ = Истина отменяет запись целиком
ПередЗаписьюНаСервереформа, сервердописать в объект то, что знает только форма
ОбработкаПроверкиЗаполненияобъект, серверпроверить и не дать записать неполное
ПередЗаписьюобъект, серверпересчитать суммы, заполнить служебное
ОбработкаПроведенияобъект, сервердвижения по регистрам — только здесь
ПриЗаписиобъект, серверсвязанные данные; ещё внутри транзакции
ПриЗаписиНаСервереформа, серверто же, но если нужна форма; тоже в транзакции
ПослеЗаписиНаСервереформа, серверперечитать и обновить форму по записанному
ПослеЗаписиформа, клиентоповестить другие формы, сказать что-то человеку

Из этого порядка следуют три правила, которые снимают большинство вопросов.

Проверки — в модуле объекта, а не формы. Объект записывают не только из формы: его пишет обмен, обработка, другой код. Проверка в модуле формы защищает ровно один способ записи из пяти.

«Всё записалось, а потом откатилось» — это транзакция. Всё до ПослеЗаписиНаСервере идёт внутри неё. Ошибка на любом шаге отменяет всё, включая движения. Поэтому отправлять письмо или дёргать чужой сервис внутри транзакции нельзя: письмо уйдёт, а документ откатится.

Форма и объект — разные данные. В ПередЗаписьюНаСервере приходит ТекущийОбъект: писать надо в него, а не в Объект формы. Запись в реквизит формы на этом шаге просто пропадёт.

Отказ = Истина — и больше ничего не пишем

У всех событий записи есть параметр Отказ. Присвоили ему Истина — запись не состоится.

Но это не Возврат: строки после присваивания выполнятся. Поэтому либо ставят Отказ и сразу выходят, либо пишут проверки так, чтобы дальше ничего вредного не случилось.

И главное — отказ надо объяснить. Молчаливый отказ выглядит как поломка: человек жмёт «Записать», ничего не происходит, и он жмёт ещё раз.

Проверка, которая показывает, ГДЕ ошибка

&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
	Если НЕ ЗначениеЗаполнено(Объект.Склад) Тогда
		ОбщегоНазначенияКлиентСервер.СообщитьПользователю(
			"Заполните склад", , "Объект.Склад", , Отказ);
		Возврат;
	КонецЕсли;
КонецПроцедуры
ЧастьЧто означает
"Объект.Склад"путь к реквизиту: платформа подсветит поле и поставит курсор в него
последний параметртот же Отказ — сообщение и отмена записи одной строкой
Возвратпосле Отказ выходим сразу: присваивание не прерывает процедуру
где лучшедля реквизитов объекта — ОбработкаПроверкиЗаполнения в модуле объекта: сработает при любой записи, не только из формы

На чём спотыкаются

Пытаются задать вопрос в ПриСозданииНаСервере. Там нет человека: форма ещё не показана. Вопросы — только из клиентских событий.

Меняют Объект в ПослеЗаписи. Поздно: документ уже записан, правка в базу не попадёт и потеряется молча.

Читают Элементы на сервере без контекста. Форму туда не везли — элементов нет. Ошибка звучит как «переменная не определена».

Пишут обработчик, но не привязывают его к событию. Процедура с подходящим именем не вызывается сама: обработчик назначают в свойствах элемента, иначе он просто лежит в модуле.

Программное изменение формы

Иногда состав формы заранее неизвестен: колонки зависят от настроек, кнопки — от прав. Тогда элементы создают кодом в ПриСозданииНаСервере: Элементы.Добавить(Имя, Тип, Родитель).

Имя элемента при этом должно быть уникальным и без пробелов — по нему к элементу обращаются. Родитель определяет, куда элемент попадёт: в группу, в командную панель, прямо на форму.

Важно: программно добавленный элемент живёт только у этой открытой формы. В конфигурации его нет, и при следующем открытии он создастся заново.

Реквизит формы против реквизита объекта

Эти два слова звучат одинаково и означают разное. Различие спрашивают на собеседовании, и оно же объясняет половину «почему не сохраняется».

Реквизит объекта — поле документа или справочника. Живёт в базе, попадает на форму через реквизит Объект и сохраняется при записи.

Реквизит формы — переменная формы. Живёт только пока форма открыта, в базу не попадает и после закрытия исчезает.

Отсюда правило: то, что должно сохраниться, кладут в объект; то, что нужно только на экране — галочку «показать подробности», временный список, флаг «уже спрашивали» — в реквизит формы.

Где что лежит

Объект.Склад            // реквизит ДОКУМЕНТА — сохранится
Объект.Товары           // табличная часть документа
ПоказатьВсе             // реквизит ФОРМЫ — исчезнет с формой
Элементы.Склад.Видимость  // не данные, а ВИД элемента
ЭтаФорма.ТолькоПросмотр   // состояние самой формы
ЧастьЧто означает
Объект.…данные, которые едут в базу; имя «Объект» — это основной реквизит формы
ПоказатьВсесвоё поле формы: добавляется в конфигураторе на закладке реквизитов
Элементы.…внешний вид: видимость, доступность, заголовок, подсказка
перепуталиЭлементы.Склад — это ПОЛЕ на экране, а не значение склада; значение — в Объект.Склад

Таблица значений на форме

Положить таблицу значений в реквизит формы можно, но работать с ней как с таблицей — нельзя. На форме она превращается в данные формы: у них свой тип и нет методов вроде Свернуть или НайтиСтроки.

Поэтому и существуют две функции-переходника: РеквизитФормыВЗначение — получить настоящую таблицу, поработать методами; ЗначениеВРеквизитФормы — положить результат обратно.

Делают это на сервере: на клиенте настоящей таблицы значений не существует вовсе, про что была глава «Таблица значений».

Динамический список — это не таблица

Список документов на форме — это ДинамическийСписок, и данных в нём нет: он читает базу порциями, по мере прокрутки.

Поэтому «пройти по списку циклом» не получится, а «посчитать итог по колонке» — тем более. Всё это делают запросом по тем же данным.

Зато у него есть то, чего нет у таблицы: произвольный текст запроса, отборы, условное оформление и порядок — всё настраивается, и всё уезжает в базу, а не применяется после.

Диалоги: почему Вопрос() больше не пишут

Самая частая находка в старом коде и самая частая ошибка при переносе.

Вопрос() и Предупреждение() останавливают выполнение и ждут человека. В веб-клиенте так нельзя: браузер не умеет замораживать страницу посреди кода. Поэтому в конфигурациях ставят режим «не использовать модальность», и эти команды перестают работать вовсе.

Замена устроена наоборот: код не ждёт ответа, а говорит, куда вернуться, когда ответ появится. Процедура разрывается на две — до вопроса и после него.

Вопрос без остановки

&НаКлиенте
Процедура Очистить(Команда)
	Оповещение = Новый ОписаниеОповещения("ОчиститьЗавершение", ЭтотОбъект);
	ПоказатьВопрос(Оповещение, "Очистить все строки?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры

&НаКлиенте
Процедура ОчиститьЗавершение(Ответ, ДопПараметры) Экспорт
	Если Ответ = КодВозвратаДиалога.Да Тогда
		Объект.Товары.Очистить();
	КонецЕсли;
КонецПроцедуры
ЧастьЧто означает
ОписаниеОповещенияимя процедуры и модуль, в котором её искать; третьим параметром можно передать свои данные
ПоказатьВопросвозвращает управление СРАЗУ — строки после него выполнятся до ответа человека
ОчиститьЗавершениесюда платформа придёт, когда человек ответит; первым параметром — его ответ
на новых платформахесть и асинхронные варианты с Ждать — код снова читается сверху вниз, но правило «клиент не замирает» то же

Код после ПоказатьВопрос выполняется сразу

Главная ловушка перехода. Человек переписывает Вопрос() на ПоказатьВопрос(), а остальное оставляет как было — и получает код, который делает своё дело до ответа.

Выглядит это издевательски: спросили «удалить?», человек ещё читает вопрос, а строки уже удалены.

Правило простое: всё, что зависит от ответа, переезжает в процедуру-продолжение. Ничего, кроме самого вызова, после ПоказатьВопрос остаться не должно.

Оповещения: как формы разговаривают

Открыли форму подбора, человек выбрал товар — как вернуть его в документ? Формы не видят друг друга напрямую.

Способов два. ОповеститьОВыборе — форма выбора отдаёт значение той, что её открыла; принимается в ОбработкаВыбора. Это штатный путь для «выбрать одно значение».

Оповестить — широковещательное сообщение всем открытым формам: «данные изменились». Принимается в ОбработкаОповещения. Так список узнаёт, что надо обновиться, когда где-то записали документ.

Второе — именно уведомление, а не команда: форма сама решает, интересно ли ей это событие.

Условное оформление и функциональные опции

Две вещи, которые новичок делает кодом, а надо настройкой.

Условное оформление — «просроченные строки красным». Настраивается в форме или в динамическом списке, работает само, без единой строки кода и без перерисовки по событиям.

Функциональные опции — «если учёт по складам выключен, полей склада нет нигде». Это не Видимость = Ложь в двадцати формах, а один флаг, к которому привязаны реквизиты, команды и целые подсистемы.

Признак, что вы делаете чужую работу: пишете один и тот же Элементы.Что.Видимость в нескольких формах.

Параметры формы и ключ уникальности

Форму открывают с параметрами: «открой список, отобранный по этому складу», «открой новый документ, заполненный из этого заказа».

Параметры приходят в ПриСозданииНаСервере в коллекции Параметры и читаются только там: дальше их уже нет. Это регулярно удивляет — код в другом обработчике обращается к Параметры и получает пустоту. Нужное значение сохраняют в реквизит формы сразу при создании.

Второе, что надо знать про открытие, — ключ уникальности. Платформа не открывает вторую форму того же объекта: она показывает уже открытую. Ключ и определяет, что считать «той же».

Отсюда обе беды: открыли форму подбора дважды и получили одну (ключ совпал), или получили две одинаковые формы отчёта, где ждали одну (ключ разный). Управляют этим последним параметром ОткрытьФорму.

«Данные уже редактируются»

Как только человек меняет что-то на форме объекта, платформа блокирует этот объект от правки в других сеансах. Не при записи — при первом изменении.

Поэтому сообщение «Данные уже редактируются» означает ровно одно: кто-то открыл этот документ и что-то в нём тронул. Часто это тот же человек, забывший вторую вкладку или ушедший обедать.

Из кода блокировку тоже можно взять явно — и это единственный честный способ написать «прочитал, посчитал, записал» без того, чтобы двое затёрли работу друг друга.

Упражнение 2. Куда положить значение

Напишите Куда(Что): «склад» — сохраняется в базе → Объект; «галочка» — нужна только на экране → реквизит формы; «видимость» — внешний вид поля → Элементы. Проверка спрашивает про «галочку».

Решается в тренажёре: код запускается и проверяется сразу.

Форма списка и форма элемента — разные формы

У справочника и документа форм несколько, и у каждой свой модуль: форма элемента, форма списка, форма выбора, форма группы.

Код, написанный в форме элемента, в списке не работает — и наоборот. «Почему при открытии из списка не срабатывает» обычно значит, что открылась другая форма.

Какая именно откроется, решают основные формы в свойствах объекта. Там же обнаруживается, что у объекта десять форм, а основной назначена не та.

Упражнение 1. Спрятать и очистить

Функция ОтборПослеСкрытия(Видимость, Значение) возвращает значение отбора, которое должно остаться после смены видимости поля. Поле видно — отбор остаётся как есть. Поле спрятали — отбор обязан стать пустой строкой: иначе он продолжит действовать невидимо.

Решается в тренажёре: код запускается и проверяется сразу.

Упражнение 3. Где проверять заполнение

Проверку обязательных реквизитов можно написать в модуле формы, а можно в модуле объекта. Функция СработаетВсегда(ГдеПроверка) получает строку "форма" или "объект" и должна вернуть Истина для того места, где проверка сработает при любой записи — из формы, из обработки, при загрузке из обмена.

Решается в тренажёре: код запускается и проверяется сразу.

Короткий свод

Реквизит объекта хранится в базе, реквизит формы живёт, пока форма открыта. Данные — через Объект, внешний вид — через Элементы. Добавленный реквизит сам на форме не появится. Спрятали поле — очистите значение. ПриСозданииНаСервере знает базу и не знает человека, ПриОткрытии — наоборот. Отказаться от записи можно в «перед», в «после» уже поздно.

Как закрепить

Читать про код и писать код — разные умения. В тренажёре к этой главе идут упражнения: вы пишете решение, оно запускается на настоящем интерпретаторе 1С и проверяется сразу, без установки платформы.

Открыть эту главу в тренажёре

Клиент и сервер: директивы компиляции
Права, роли и RLS