Интерактивное представление
Форма, открытая в интерактивном режиме, представляет из себя графический компонент определенного дизайна, в котором пользователь может инициировать различные события, и тем самым осуществлять навигацию по объектам системы, просматривать и изменять значения свойств, выполнять действия и так далее. Также для этого представления разработчику доступен дополнительный набор операторов, позволяющий управлять открытой формой.
Представления объектов
В интерактивном представлении группы объектов могут отображаться в виде таблицы. Рядами в этой таблице являются наборы объектов, а, соответственно, колонками - свойства. Записи, которые будут отображаться в таблице и их порядок определяются действующими на момент отображения фильтрами и сортировками.
Изменение текущих значений объектов может происходить либо в результате выполнения действия, созданного при помощи специального оператора поиска (ACTIVATE), либо в результате изменения текущего ряда, если группа объектов отображается в виде таблицы.
При отображении группы объектов в виде таблицы количество отображаемых рядов (наборов объектов) может как определяться автоматически на основе высоты видимой части таблицы, так и задаваться разработчиком явно при создании формы.
У кнопки экспорта таблицы в XLSX есть запасной вариант на случай слишком большого результата. Включает его рабочий параметр minSizeForReportExportToCSV: по умолчанию он равен -1, и запасного варианта нет, а при нуле или больше платформа сравнивает с ним размер собранных данных в байтах и, если данные больше, возвращает вместо XLSX те же данные файлом CSV с разделителем ;.
Деревья объектов
В платформе также существует возможность, отображать несколько групп объектов в одной таблице одновременно. Происходит это по аналогии с иерархией групп объектов в статичном представлении, то есть, если у нас есть две группы A и B, то в "объединенной" таблице сначала показывается первый набор объектов из A, затем все наборы объектов из B (подходящие под фильтры), после этого второй набор объектов из A, затем опять все наборы объектов из B и так далее. При этом, крайне желательно, чтобы фильтры для B использовали все объекты из A, так как иначе теряется смысл объединения таких групп в одно дерево. Первоначально, при открытии формы в таблице, отображаются объекты только самой верхней группы объектов, но при этом слева в таблице создается специальная колонка, при помощи, которой пользователь может самостоятельно раскрывать его узлы, и тем самым просматривать только интересующие его объекты в нижних группах объектов. Еще одной функцией создаваемой колонки, является демонстрация вложенности узлов за счет табуляции элементов внутри этой колонки (это позволяет пользователю лучше понимать на каком уровне иерархии он сейчас находится).
Деревья объектов также можно использовать для отображения иерархических данных (например классификаторов). В этом случае потомками набора объектов группы в дереве могут являться не только наборы объектов нижних групп, но и наборы объектов этой же группы (будем называть, такую группу объектов иерархической). Чтобы определять эти дочерние наборы объектов в такой иерархической группе объектов, для нее необходимо задать дополнительный фильтр, который в отличие от обычных фильтров, может обращаться не только к значениям фильтруемого набора объектов, но и к значениям "верхнего по дереву" набора объектов (эта техника очень похожа на аналогичную в операторе рекурсии). При этом, крайне желательно, чтобы иерархический фильтр использовал все значения верхних наборов объектов, так как иначе, как и для фильтров между различными группами объектов, теряется смысл создания такого дерева. Первоначально считается, что все значения "верхнего по дереву" набора объектов равны NULL.
В текущей реализации платформы иерархические группы позволяют отображать только деревья (а не направленные графы). Соответственно, в качестве иерархического фильтра можно использовать только равенства значения верхнего набора объектов и свойства, принимающих на вход нижние (фильтруемые) значения объектов (так гарантируется что к одному узлу дерева нельзя придти различными путями)
Свойства различных групп объектов в дереве выстраиваются в колонки друг под другом, то есть первая колонка отображает первые свойства каждой группы объектов, вторая колонка - вторые и так далее. При этом общее количество колонок дерева определяется последней группой объектов этого дерева (все "лишние" свойства верхних групп просто игнорируются).
Представления свойств
Любое свойство или действие может отображаться на форме в одном из следующих представлений:
- Панель (
PANEL) - отдельный компонент, который отображает заголовок свойства на форме и значение этого свойства для текущих значений объектов формы. - Тулбар (
TOOLBAR) - аналогично панели, только этот компонент имеет другое расположение по умолчанию (сразу под таблицей), и, если таблица, к которой относится этот тулбар, скрывается, тулбар скрывается вместе с ней. - Всплывающая панель (
POPUP) - аналогично панели, только компонент размещается в сворачиваемом всплывающем блоке, который пользователь открывает отдельной кнопкой; подходит для свойств, которые нужно показывать только по необходимости. - Колонка таблицы (
GRID) - отдельная колонка в таблице, которая отображает текущие значения свойства для всех наборов объектов (рядов) в таблице.
Для каждой группы объектов можно задать, в каком представлении по умолчанию будут отображаться свойства этой группы (по умолчанию таким представлением является колонка таблицы). Если у свойства нет параметров (то есть у него отсутствует группа отображения), оно отображается в панели. Действия всегда, по умолчанию, отображаются в панели.
В оставшейся части раздела поведение свойств и действий абсолютно одинаково, поэтому дальше будем использовать только термин свойство (для действий поведение абсолютно идентично).
При необходимости разработчик может явно указать, какое представление должно использовать отображаемое свойство.
Если в таблицу группы объектов в какой-то момент времени не отображается ни одно свойство, она автоматически скрывается.
По умолчанию, в качестве заголовка каждого свойства на форме используется заголовок самого свойства. При необходимости, разработчик может указать другое значение заголовка, или, если нужна еще большая гибкость, использовать в качестве заголовка свойство, которому, в свою очередь, можно передавать на вход верхние объекты отображаемого свойства. Также, стоит отметить, что если для свойства заданы группы-в-колонки, то для создаваемых колонок желательно иметь разные заголовки (чтобы как-то их различать), поэтому, в этом случае, рекомендуется в качестве заголовка использовать свойство, которое принимает на вход все (!) объекты заданных групп-в-колонки.
Кроме заголовков, для каждого представления свойства на форме можно задать цвета (как цвет фона, так и цвет текста), а также условие, при невыполнении которого свойство не будет отображаться. Как и заголовок, каждый из этих параметров задается при помощи некоторого свойства.
После изменения, сделанного в сессии формы, форма заново читает только представления, чьё свойство зависит от изменившихся данных, а также представления групп объектов, у которых изменились объекты или строки; остальные представления сохраняют свои значения.
Свойство, значением которого является объект пользовательского класса, отображает лишь внутренний идентификатор этого объекта — число, ничего не говорящее пользователю. Поэтому на форму обычно выводится не сама связь с объектом, а композиция по ней — например, заголовок связанного объекта; запись через такую композицию передаётся в связь, так что при редактировании пользователь выбирает связанный объект. Если композиция проходит через несколько связей подряд (name(agent(listing(i))) на форме с объектом i), запись уходит в первую от объекта формы связь — listing(i): пользователю предлагаются объекты класса этой связи (Listing), отображённые остатком цепочки (name(agent(l))), а не объекты за дальнейшими связями; сами дальнейшие связи (agent(...)) и их объекты не меняются.
Если такая композиция по связи изменяема, а её внешняя часть — представление вариантов, например заголовок связанного объекта, — уникальна среди подходящих объектов, веб-клиент показывает значение как элемент выбора: перечень всех объектов, которые предлагал бы диалог выбора, отображённых этим представлением; пользователь выбирает один из них, и выбранный объект записывается в связь. Хранимое свойство, чьё собственное имя в camelCase читается как name, id, number или caption, считается уникальным сразу, без обращения к какой-либо статистике, — под это подпадает как раз типичная композиция вроде заголовка из примера выше. Для любого другого свойства уникальность вариантов платформа доверяет только при числе подходящих объектов, строго превышающем minInterfaceStatForValueUnique (по умолчанию 100): при этом значении или меньшем статистике по числу строк доверия недостаточно, чтобы ответить определённо, и элемент выбора не строится. При большем количестве вид элемента определяется рабочими параметрами: если суммарная длина представлений всех вариантов не превышает maxLengthForValueButton (по умолчанию 40), а в колонке таблицы — maxLengthForValueButtonGrid (по умолчанию 10), рисуется группа кнопок — примыкающие друг к другу кнопки-переключатели, в которой выбранный вариант подсвечен; иначе вне колонки таблицы при числе вариантов не больше maxInterfaceStatForValueList (по умолчанию 3) — список, столбец переключателей (в горизонтальном контейнере — та же группа кнопок); иначе при числе вариантов не больше maxInterfaceStatForValueDropdown (по умолчанию 20) — выпадающий список, стандартный элемент select, показывающий выбранный вариант и раскрывающий остальные по нажатию, без ввода текста; при большем числе вариантов элемент выбора не строится, и используется обычное редактирование. Если свойство может быть NULL, элемент позволяет и сбросить значение: повторное нажатие выбранного варианта (в выпадающем списке — пустой вариант) записывает NULL. Для свойства, показанного только для чтения, элемент выбора автоматически не строится. Вид элемента можно также задать явно или отключить в дизайне формы.
С этими порогами сравнивается не фактическое число вариантов, а его оценка по статистике условия, отбирающего варианты, и суммарная длина, оценённая как это число, умноженное на среднюю длину значения класса представления; если оценка числа вариантов превышает предел выпадающего списка, элемент не строится. Фактические число и длина читаются вместо оценки, только если представление вариантов не зависит от текущих объектов формы, а условие либо тоже не зависит от них, либо оценка числа отбираемых им объектов не меньше оценки числа объектов всего класса (тогда читается весь класс — кроме явно заданного ввода без начального значения, где в этом случае остаётся оценка). Оценка груба и может заметно отличаться от фактического числа подходящих объектов, так что элемент бывает построен и при большом фактическом числе вариантов (тогда все они в нём показываются), и не построен при малом. Элемент выбора строится и для свойства, действие изменения которого задано явно как ввод значения объекта пользовательского класса с набором предлагаемых значений: вариантами становятся объекты этого набора, отобранные также постоянными фильтрами формы выбора класса, а при вводе для изменения свойства — по умолчанию и ограничениями; представлением — заданное для набора отображаемое значение, уникальность которого при этом не проверяется; вид элемента определяется теми же порогами. Если у ввода не задано начальное значение, выбранный вариант определяется по значению самого показанного свойства, поэтому элемент строится, только когда класс этого значения — встроенный и совместим с классом отображаемых значений. Если элемент не построен, объект вводится в редакторе класса отображаемого значения с подсказками из набора либо выбирается в форме выбора. Отключение элемента выбора возвращает обычное выполнение действия изменения — для такого ввода это ввод в поле с подсказками; чтобы сразу открывалась форма выбора, её должно открывать само действие изменения.
Действия по умолчанию для свойства, значением которого является объект, - редактирование этого объекта или выбор другого в диалоге - строятся для любого такого свойства, представление которого можно менять; рабочий параметр onlyUniqueObjectEvents (по умолчанию false) ограничивает их свойствами с уникальным представлением, что может оставить без них очень многие свойства. Эти действия строятся и запоминаются при подготовке форм, поэтому изменение параметра вступает в силу только после перезапуска сервера.
В десктоп-клиенте showNotDefinedStrings (по умолчанию false) помечает по-настоящему отсутствующее (NULL) строковое значение явной пометкой вместо пустой ячейки: "Not defined", либо "Required", если свойство обязано быть заполнено. С пустой строкой всё наоборот: при выключенном параметре она помечается как "Empty string", чтобы её не спутали с пустой ячейкой; при включённом параметре она, наоборот, отображается как настоящая пустая ячейка.
Выбор нескольких значений строится для группировки, сцепляющей строки (CONCAT), когда сцепляемое представление берётся при условии, а само условие — одно изменяемое логическое свойство от группируемого объекта и параметров результата, чаще всего первичное: in = DATA BOOLEAN (Book, Tag) и tags(Book b) = GROUP CONCAT name(Tag t) IF in(b, t), ', ' ORDER name(t), t (условие можно перенести и в отбор элементов группы: GROUP CONCAT name(Tag t), ', ' ORDER name(t), t WHERE in(b, t)). Веб-клиент показывает такое свойство не строкой, а элементом выбора над объектами, у которых представление не NULL; пользователь отмечает и снимает варианты, каждое переключение записывает в условие TRUE либо NULL для этого объекта, а сцепленная строка лишь пересчитывается. Условием может быть и композиция с путём записи (in(b, t) AND active(t) — запись уходит в in), но не цепочка из нескольких условий (name(t) IF active(t) IF in(b, t)) и не свойство без пути записи; сцепление только части группы (TOP) элементом выбора не показывается. Вид элемента определяется теми же рабочими параметрами и статистикой, что и для одиночного выбора: при суммарной длине представлений не больше maxLengthForValueButton (в колонке таблицы — maxLengthForValueButtonGrid) — отдельные кнопки-переключатели; иначе вне колонки таблицы при числе вариантов не больше maxInterfaceStatForValueList — столбец флажков (в горизонтальном контейнере — те же кнопки); иначе при числе вариантов не больше maxInterfaceStatForValueDropdown — выпадающий список, в котором отмечается несколько значений; при большем числе — текстовое поле с выпадающим списком: варианты не загружаются целиком, а подбираются по введённому тексту. Перечень вариантов задаёт само сцепляемое представление, поэтому, чтобы предлагались не все объекты класса, представление берётся при дополнительном условии в собственных скобках: GROUP CONCAT (name(Tag t) IF active(t)) IF in(b, t), ', ' ORDER name(t), t — предлагаются и сцепляются только подходящие объекты (уже отмеченный объект, переставший подходить, остаётся в элементе отмеченным, но без надписи). Явно заданное действие изменения заменяет встроенное редактирование: выбор нескольких значений для такого свойства не строится.
Группы фильтров
Для того чтобы предоставить пользователю интерфейс по выбору применяемых фильтров, их можно объединить в группы фильтров. Для каждой из этих групп на форме будет создан специальный компонент, при помощи которого пользователь сможет выбирать один фильтр из группы в качестве текущего активного. Если несколько фильтров в одной группе применяются к разным группам объектов, то компонент будет отображаться для последней из них. Кроме заданных фильтров компонент по умолчанию содержит пункт (Все), позволяющий не применять ни один из фильтров группы.
Для каждой группы фильтров разработчик может задать имя, при помощи которого к нему можно будет обращаться в дальнейшем (например в дизайне формы).
Пользовательские фильтры / сортировки
Пользователь может добавлять свои / изменять существующие сортировки, а также добавлять свои фильтры при помощи соответствующих интерфейсов:
- Сортировки - путем двойного нажатия на заголовке колонки.
- Фильтры - при помощи соответствующей кнопки под таблицей каждой группы объектов. По умолчанию, фильтр устанавливается на активное свойство в таблице, и для всех типов данных, кроме строковых регистронезависимых, фильтрует на равенство введенному значению (для строковых регистронезависимых типов фильтр ставится на включение введенной строки). При необходимости, разработчик может задать тип фильтрации по умолчанию явно при помощи соответствующей опции.
Разработчик может и заранее объявить пользовательский фильтр для свойства формы: тогда строка его условия показывается в компоненте пользовательского фильтра группы объектов этого свойства сразу при открытии формы - с заданными свойством и сравнением и пустым значением - и ничего не отфильтровывает, пока пользователь не введёт значение. Такая строка постоянная: пользователь меняет её значение, а при показанных элементах управления фильтрами - и сравнение, но удалить её не может, а сброс всех фильтров лишь очищает её значение. Строка показывается, только пока её компонент присутствует в дизайне формы, где его можно переместить или удалить.
Когда несколько фильтров объединены через И/ИЛИ, по умолчанию граница между двумя условиями рисуется как разделитель-глиф (вертикальная черта); рабочий параметр useTextAsFilterSeparator рисует вместо неё кликабельную надпись "AND"/"OR", по нажатию на которую граница переключается.
Список асинхронного ввода значений
Ввод текста в список значений, объявленный для свойства, в поле фильтра или в поле поиска с автодополнением запускает запрос к базе данных на каждое нажатие клавиши, а не фильтрует уже загруженный список; семейство рабочих параметров настраивает построение этого запроса и то, сколько строк он может прочитать.
По умолчанию введённый текст сопоставляется с вариантами полнотекстовым MATCH; inputListSearchInsteadOfContains, установленный в false, переключает его на обычное вхождение подстроки (CONTAINS). В режиме MATCH символ, разделяющий несколько условий поиска, введённых в одно поле, — matchSearchSeparator (по умолчанию ,); клиент также показывает его в подсказке к полю поиска и экранирует, если пользователь вводит его буквально. Если у списка значений нет явного ORDER, результаты читаются по стратегии постепенного увеличения LIMIT, которая останавливается, как только найдено достаточно вариантов; inputListOrderByRank отключает это и вместо этого выполняет один запрос, всегда упорядочивающий варианты по полнотекстовой релевантности.
Саму стратегию постепенного чтения задают: asyncValuesNeededCount (по умолчанию 15) - сколько вариантов нужно показать в выпадающем списке; asyncValuesMaxReadCount (1000) - до какого значения может вырасти эта оценка, прежде чем платформа перестанет ограничивать чтение и выполнит запрос без ограничения; и asyncValuesExtraReadCoeff (1.5) - множитель запаса поверх оценённой доли подходящих строк, применяемый также, геометрически, для увеличения LIMIT на каждой повторной попытке, если найдено слишком мало различных вариантов. Если оценённое число строк лежащего в основе отношения превышает asyncValuesMaxReadOrderCount (1000), запрос вовсе отказывается от ORDER BY ради производительности. Такой список значений предлагается автоматически, если список вариантов свойства обходится строго дешевле asyncValuesMaxReadDataCompletionCount строк (по умолчанию 100000) — кроме свойств типов текст/JSON/XML: для них это автоматическое поведение не действует, и список значений появляется только при явной аннотации inputlist, независимо от стоимости; при этой стоимости или выше аннотация нужна для любого свойства. Аннотация noinputlist всегда отключает список значений. Для отношения с оценённым размером не меньше asyncValuesTooShortDataCompletionCount строк (100000) ввод asyncValuesTooShortThreshold символов или меньше (по умолчанию -1, то есть эта защита выключена) возвращает "String too short to search" вместо выполнения запроса, защищая большие таблицы от почти полного сканирования на каждое нажатие клавиши.
Если пользователь продолжает набирать текст, пока предыдущий запрос для того же поля ещё выполняется, платформа по умолчанию прерывает поток этого запроса, чтобы освободить занятые им ресурсы; disableAsyncValuesInterrupt отключает это для JDBC-драйверов, не переносящих прерывание посреди запроса. Для автодополнения значения самого фильтра useGroupFiltersInAsyncFilterCompletion определяет, сужают ли варианты дополнительно другие действующие в этот момент фильтры той же группы объектов: 0 - никогда не учитывать их, 1 - учитывать, но платформа может всё равно оптимизировать это учитывание, 2 (по умолчанию) - учитывать всегда.
Выбор объектов по умолчанию
В интерактивном представлении формы фильтры для групп объектов могут изменяться в результате различных действий пользователя (например, при изменении верхних объектов этих фильтров, выборе фильтров в группе фильтров и т.п.), после чего текущие объекты могут уже не удовлетворять условиям новых фильтров. Кроме того, при открытии формы, некоторые объекты могут не передаваться, или передаваться равными NULL. В обоих этих случаях, для группы объектов необходимо изменить текущие объекты, на некоторые текущие *объекты по умолчанию. *В платформе существует несколько вариантов выбора новых текущих объектов:
- Первый (
FIRST) - первый набор объектов (в соответствии с текущим порядком) - Последний (
LAST) - последний набор объектов. - Предыдущий (
PREV) - предыдущий (или максимально близкий к нему) набор объектов. - Не определено (
NULL) - наборNULLзначений.
Если ни одна из этих опций не задана явно, платформа пытается сама определить являются ли постоянные фильтры в группе объектов а) взаимоисключающими при различных значениях верхних объектов (если таковые имеются), и / или б) фильтр отбирает очень малый процент от общего числа объектов заданных классов. В обоих этих случаях нет смысла искать предыдущий объект и, по умолчанию, выбирается первый (FIRST) объект, соответственно, во всех остальных случаях - предыдущий (PREV).
Для объекта встроенного класса в панели, значения которого не ограничены фильтрами группы объектов, при любом типе объектов по умолчанию, кроме NULL, если значение объекта не передано при открытии формы и у него нет предыдущего значения, сохраняемого типом PREV, текущим значением становится значение по умолчанию его класса: например, объект класса DATE при открытии формы инициализируется текущей датой. Чтобы оставить такой объект равным NULL, для группы объектов задается тип объектов по умолчанию NULL.
Пока у объекта пользовательского класса нет текущего значения — задан тип объектов по умолчанию не определено, либо ни один набор объектов не подходит под действующие фильтры (в частности, объектов класса еще нет), — представления свойств и действий, получающих этот объект на вход, на форме не показываются, где бы они ни были размещены. Это относится к представлениям, использующим текущее значение объекта: если значения объекта берутся из строк таблицы или группы-в-колонки, отсутствие текущего значения само по себе представление не скрывает и подходящие строки не убирает. Свойства и действия, не получающие этот объект, — например, действие создания объекта — остаются. Не скрываются по этому основанию также свойства с условием отображения и свойство-селектор, через которое пользователь выбирает объект; но само по себе это не делает возможным обычное изменение свойств отсутствующего объекта — введенное значение не сохраняется. Поэтому, чтобы все поля карточки объекта были доступны с самого открытия, объект создают до ее открытия, а не строят карточку на объекте без текущего значения; если объект создается действием создания на уже открытой форме, его поля становятся доступны после создания.
Стоит отметить, что выбор объектов по умолчанию по сути эквивалентен выполнению операции поиска объектов, при этом объектами поиска являются:
- для типа
PREV- при открытии формы - либо переданные объекты, либо, если их нет, последние использованные объекты для класса объекта формы — объекты этого класса (или его потомков), бывшие текущими в интерактивных формах того же соединения: объект, который последним был у этой группы объектов на этой форме, иначе — тот, что последним был у другой группы этой формы, иначе — последний бывший текущим на любой форме.
- в остальных случаях - предыдущие текущие значения объектов
- для остальных типов
- при открытии формы - переданные объекты
- в остальных случаях - пустое множество объектов
Направление поиска определяется типом объекта по умолчанию (PREV при этом эквивалентен FIRST).
Операторы работы с объектами
При добавлении свойств на форму, вместо конкретных свойств можно использовать предопределенный набор операторов, которые реализуют наиболее частые сценарии работы с объектами (таким образом нет необходимости создавать и именовать такие свойства каждый раз за пределами формы):
- Значение объекта (
VALUE) - для встроенных классов будет добавлено специальное свойство с одним аргументом, которое показывает текущее значение объекта и позволяет пользователю его изменять. Для пользовательских классов добавится свойство, которое показывает идентификатор объекта в базе данных, а при попытке изменения показывает диалог со списком соответствующих объектов. Выбранное значение будет использовано как текущее значение объекта на форме. - Интервал объекта (
INTERVAL) - для пары объектов формы одного и того же класса даты или времени добавляется одно свойство, которое отображает интервал между ними и, если оба объекта показаны в панели, позволяет пользователю его изменять; при изменении интервала первый объект становится его нижней границей, а второй - верхней. - Создание нового объекта (
NEW) - добавляет действие без аргументов, которое добавляет объект класса переданного объекта формы (или класса явно заданного разработчиком), после чего автоматически делает этот объект текущим. Если у класса есть потомки, то пользователю будет выдан диалог с возможностью выбора. Если на форме к объекту, для которого происходит добавление, применяются какие-нибудь фильтры, то система попытается вновь созданному объекту изменить свойства таким образом, чтобы он удовлетворял этим фильтрам (как правило, для добавленных объектов в каждый из фильтров записывается значение по умолчанию класса значения этого фильтра) - Редактирование объекта (
EDIT) - добавляет действие с одним аргументом, которое вызывает действиеSystem.formEdit(которое, в свою очередь, по умолчанию открывают форму редактирования объекта соответствующего класса). - Создание и редактирование объекта (
NEWEDIT) - добавляет действие без аргументов, которое добавляет объект соответствующего класса, вызывает действие редактирования объекта (EDIT), и если ввод не был отменен, устанавливает добавленный объект как текущий. - Удаление объекта (
DELETE) - добавляет действие с одним аргументом, при выполнении которого удаляется текущий объект.
Также для последних четырех операторов можно задать опции (для всех остальных действий эти опции игнорируются):
- Новая сессия (
NEWSESSION) - в этом случае добавляемое на форму действие будет выполняться в новой сессии. При открытии форм в новой сессии важно помнить, что в них не будут видны изменения сделанные в текущей сессии (форме). Таким образом, этот механизм рекомендуется использовать только в случае, если открытие происходит из формы, в которой пользователю ничего нельзя изменять, или обе формы никак не пересекаются по свойствам или объектам. Отметим, что при использовании в новой сессии оператора создания нового объекта (NEW), вместо просто создания нового объекта выполняется создание и редактирование объекта (NEWEDIT) (в противном случае сессия сразу же закроется и изменения потеряются). - Вложенная сессия (
NESTEDSESSION) - действие будет выполняться в новой вложенной сессии. Также как и в новой сессииNEWзаменяется наNEWEDIT.
Формы выбора / редактирования
Для каждой формы можно указать, что она является формой по умолчанию для выбора / редактирования объектов заданного класса. В этом случае, при вызове действий, созданных при помощи операторов работы с объектами (создание / редактирование объекта), будет открываться именно эта форма. Эта же форма будет открываться и при использовании соответствующей опции выбора формы в операторе открытия формы.
Если для класса форма выбора / редактирования не задана, платформа автоматически создаст такую форму. Эта форма будет состоять из одного объекта этого класса, а также всех свойств подходящих по классу и принадлежащих группе свойств System.base. Кроме того на эту форму автоматически будут добавлены действия создания, редактирования и удаления объекта в новой сессии, а также свойство значения объекта, если не существует ни одного свойства из группы свойств System.id, подходящего по классу этому объекту (то есть на форму не добавлено ни одного "идентификатора" объекта).
Собственник сессии
Так как форма по умолчанию открывается в текущей сессии, применять / отменять изменения этой сессии может быть не всегда безопасно, так как, например, можно случайно применить "не свои" изменения. Чтобы избегать таких ситуаций, в платформе существует такое понятие как собственник сессии - форма, которая ответственна за управление жизненным циклом сессии (например, применение / отмена изменений). По умолчанию, считается, что форма является собственником сессии, если при открытии формы у этой сессии не было других собственников.
Для реализации механизма работы с собственниками сессии в платформе используется числовое локальное свойство System.sessionOwners. Соответственно, это свойство увеличивается на 1 при открытии формы и уменьшается на 1 при ее закрытии. Таким образом, оно показывает глубину вложенности "стека открытия форм" и равно NULL если у сессии нет собственника, и не NULL в обратном случае.
В случае необходимости, разработчик при открытии формы может явно указать, что открываемая форма является собственником сессии, которую она использует.
Владение сессией влияет только на отображение / поведение системных действий управления жизненным циклом формы / сессии. При использовании остальных действий рекомендуется самостоятельно учитывать риск применить "не свои" изменения (и, к примеру, использовать все то же свойство System.sessionOwners).
Системные действия для управления жизненным циклом формы/сессии
На любую форму автоматически добавляется следующие системные действия (в скобках указаны их имена):
- Обновить (
System.formRefresh) - обновляет текущее состояние формы, перечитывая всю информацию из базы данных. - Сохранить (
System.formApply) - сохраняет в базу данных изменения, сделанные в форме. - Отменить (
System.formCancel) - отменяет все изменения, сделанные на форме. - OK (
System.formOk) - закрывает текущую форму и, если форма является собственником сессии, то применяет изменения в базу данных. - Закрыть (
System.formClose) - закрывает текущую форму, ничего не делая с изменениями. Если форма является собственником сессии и в её сессии есть неприменённые изменения хранимых данных (а сама форма не показана как редактор по месту), у пользователя сначала запрашивается подтверждение закрытия (см. опережающее выполнение). - Сбросить (
System.formDrop) - закрывает текущую форму и возвращает в качестве выбранного объектаNULL.
По умолчанию у перечисленных системных действий следующие условия видимости:
| Действие | Условие |
|---|---|
| Обновить | Всегда |
| Сохранить, Отменить | Если форма является собственником и на форме могут вызываться действия, которые изменяют текущую сессию. Также Отменить может не показываться, если платформа определяет, что отмена изменений гарантированно приведет к изменению начальных значений объектов формы (то есть выбору других объектов) |
| ОК, Закрыть | Если открытие формы синхронное |
| Сбросить | Если открытие формы синхронное, возвращает значение и разрешает передачу NULL значений |
При необходимости все эти действия можно отключить / включить, убрав соответствующие компоненты из дизайна формы и / или использовав соответствующие опции в операторе открытия формы.
Индикатор ожидания
Пока клиент ждёт ответа на синхронный запрос к серверу — применение изменений, выполнение действия и так далее — ничто не сообщает пользователю о выполняющемся запросе, пока не пройдёт примерно задержка, заданная рабочим параметром busyDialogTimeout (по умолчанию 1000 мс), после чего показывается индикатор ожидания; меньшее заданное значение соблюдается не точно — десктоп-клиент никогда не ждёт меньше 1000 мс, а веб-клиент вычитает из него около 500 мс собственной задержки, но не меньше 500 мс. В веб-клиенте запрос модален с момента отправки, а не с момента появления индикатора: ввод блокируется на всё время ожидания, а задерживается только индикатор — поэтому синхронный запрос короче задержки теряет события мыши и клавиатуры, и на экране ничто этого не объясняет. Веб-клиент всегда показывает модальный диалог; в десктоп-клиенте busyDialog выбирает между тем же диалогом — с кнопкой отмены и периодически обновляемым сообщением о ходе выполнения (по умолчанию) — и, если отключён, обычным курсором ожидания без возможности отмены. В десктоп-клиенте maxRequestQueueSize (по умолчанию 0, то есть без ограничения) дополнительно ограничивает, сколько собственных асинхронных запросов клиента без ожидания ответа может быть поставлено в очередь одновременно; когда очередь достигает этого размера, дальнейшие запросы, вызванные пользователем, отправляются синхронно (и тем самым попадают под действие индикатора ожидания выше) вместо накопления в очереди, защищая соединение от переполнения при быстрых повторных действиях.
В веб-клиенте сообщение сервера в лог, пришедшее пока панель логов не в фокусе, по умолчанию сопровождается всплывающим уведомлением; enableShowingRecentlyLogMessages заставляет вместо этого саму панель ненадолго раскрыться. В десктоп-клиенте по умолчанию новое сообщение никак не отмечается; тот же параметр заставляет его собственную панель логов точно так же ненадолго раскрыться.
Дополнительные возможности
Для формы можно задать файл с картинкой, которая будет отображаться в качестве иконки формы.
Также форма может автоматически обновляться через заданный промежуток времени — для этого действие System.formRefresh периодически запускается как событие формы.
При наведении на заголовок колонки таблицы в любом клиенте, либо на любой другой элемент интерфейса в веб-клиенте, всплывающая подсказка с подробной информацией показывается после времени наведения, заданного рабочим параметром tooltipDelay (по умолчанию 1500 мс; 0 отключает её). В десктоп-клиенте элемент, не являющийся заголовком колонки, всё равно ждёт фиксированные 1500 мс независимо от заданного значения — tooltipDelay там определяет только, появится ли подсказка вообще, а не то, сколько ждать.
Язык
Использование всех вышеперечисленных возможностей, как и задание структуры формы, осуществляется при помощи инструкции FORM.
Открытие формы
Для отображения формы в интерактивном представлении используется соответствующий оператор открытия формы в интерактивном представлении.
Примеры
date = DATA DATE (Order);
FORM showForm
OBJECTS dateFrom = DATE, dateTo = DATE PANEL
PROPERTIES VALUE(dateFrom), VALUE(dateTo)
OBJECTS o = Order
FILTERS date(o) >= dateFrom, date(o) <= dateTo
;
testShow () {
SHOW showForm OBJECTS dateFrom = 2010_01_01, dateTo = 2010_12_31;
NEWSESSION {
NEW s = Sku {
SHOW sku OBJECTS s = s FLOAT;
}
}
}
FORM selectSku
OBJECTS s = Sku
PROPERTIES(s) id
;
testDialog {
DIALOG selectSku OBJECTS s INPUT DO {
MESSAGE 'Selected sku : ' + id(s);
}
}
sku = DATA Sku (OrderDetail);
idSku (OrderDetail d) = id(sku(d));
changeSku (OrderDetail d) {
DIALOG selectSku OBJECTS s = sku(d) CHANGE;
//равносильно первому варианту
DIALOG selectSku OBJECTS s = sku(d) INPUT NULL CONSTRAINTFILTER DO {
sku(d) <- s;
}
}