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