XForms в Showcase

Материал из Course Orchestra
Перейти к: навигация, поиск
Showcase

Создание решений на КУРС:Showcase

Содержание

XForms

Общие сведения

Шаблон для XForms хранится на диске в каталоге XForms используемой userdata. Все кнопки на форме (выполняющие некие действия) на форме задаются разработчиками приложения. С нажатием кнопки может быть связан вызов сервлета через механизм submission и/или вызов одной из 5-х процедур JS, определенных в клиентском коде GWT.

Компонента загрузки файлов (xf:upload)

Начиная с версии 1.9.1 XForms поддерживает встраивание компоненты для загрузки файлов прямо в тело формы (без вызова дополнительного окна). А для браузеров, поддерживающих HTML 5 FileAPI возможен выбор нескольких файлов для загрузки за один раз. Тестирование показало, что HTML 5 FileAPI поддерживают все последние версии современных браузеров кроме IE.

Новая компонента загрузки файлов upload встраивается в XForms путем добавления в нужное место формы строки:

 <xf:upload id="xformId_procId" />

где procId - это id из описания процедуры типа UPLOAD в инф. панели. Каждой процедуре может соответствовать только одна компонента загрузки файлов. Процедур (и компонент загрузки файлов) на форме может быть произвольное количество. Закачка происходит при вызове gwtXFormSave. При этом необходимо наличие процедуры, которая сохраняет саму карточку.

Возможности новой компоненты загрузки файлов:

  • Быстрая загрузка файлов. При добавлении атрибута submit="true" справа от кнопки выбора файлов появляется кнопка для отправки файлов на сервер. Надпись на этой кнопке задается атрибутом submitLabel="Загрузить файлы" (при отсутствии данного атрибута будет выведен заголовок по умолчанию: "Загрузить").
  • Запрет выбора нескольких файлов. Задается атрибутом singleFile="true".
  • Передача выбранных файлов в XForm. При задании атрибута filenamesMapping="XPath(instance(quot(myInstance))/files1)" после выбора файлов для закачки, их названия будут добавлены в ноду, определяемую заданным XPath запросом, в виде
 <file>Название файла 1</file>
 ...
 <file>Название файла n</file>

Необходимость предварительной очистки данной ноды определяется атрибутом needClearFilenames="true".

После того как файл будет послан на сервер, как правило, компонент загрузки файлов очищается (вместо названия выбранного файла появляется надпись "Файл не выбран"). В некоторых случаях подобное поведение может вводить пользователя в заблуждение, поэтому в настройку <xf:upload> был добавлен атрибут notClearUpload. При установке notClearUpload="true" очистка загрузчика не происходит и сохраняется название выбранного файла.

На данный момент форма поддерживает работу старой компоненты. Более того, форма может содержать одновременно старую и новые компоненты. При этом использование старой компоненты не рекомендуется.

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

Использование submission

submission может использоваться для запроса дополнительных данных: при этом он может обращаться к сторонним приложениям, а может вызывать следующие сервлеты из Showcase:

  • secured/sqlTransform?proc=procname - обработка формы SQL-процедурой (кроме того возможно использование данного механизма для "сложного" сохранения данных - в противоположность простому сохранению данных при вызове JS функции gwtXFormSave)
  • secured/xslTransform?xsltfile=sourcename - вызов xsl трансформации на сервере
  • secured/jythonTransform?proc=procname - обработка формы Jython процедурой. Появилось в версии 2.0.9!

где procname - имя SQL процедуры на сервере, а sourcename - имя источника xsl-трансформации: это может быть xsl-файл, jython процедура или хранимая процедура SQL. При задании типа источника данных действует принцип универсальных источников.

Новые названия сервлетов введены в версии 1.8.1. Старые форматы вызова пока оставлены для совместимости, но являются deprecated. Во вновь создаваемых XForm их использование не рекомендуется!

Данный механизм работает в обход gwt-кода, но при этом в хранимых процедурах почти всегда нужна информация о текущем контексте и элементе. Поэтому при подготовке любой(!) формы на сервере в нее автоматически добавляется специальный инстанс, состоящий из 2 частей, путь к которым в формате XPath следующий:

  • /srvdata/schema/context - текущий контекст в формате:
<context>
   <additional>add context</additional>
   <filter>filter info</filter>
   <main>main context</main>
   <session>session info</session>
</context>
  • /srvdata/schema/element - описание текущего элемента в формате:
<element>
   <id>08</id>
   <position>6</position>
   <type>XFORMS</type>
   <procName>xforms_proc1</procName>
   <templateName>Showcase_Template.xml</templateName>
   <hideOnLoad>false</hideOnLoad>
   <neverShowInPanel>false</neverShowInPanel>
   <procs>
      <entry>
         <key>proc1</key>
         <value>
            <id>proc1</id>
            <name>xforms_saveproc1</name>
            <type>SAVE</type>
         </value>
      </entry>
      <entry>
         <key>proc2</key>
         <value>
            <id>proc2</id>
            <name>xforms_submission1</name>
            <type>SUBMISSION</type>
         </value>
      </entry>
   </procs>
</element>

Информацию из данного инстанса можно включать в передаваемый на сервер инстанс. При работе с submission нужно учитывать еще одну вещь. Для обращения к правильной userdata сервер должен получить идентификатор userdata от клиента. Поэтому при обработке элементов XForms на сервере происходит автоматическое добавление ко всем вызовам submission параметра userdata.

Внимание! Стандартный способ вызова submission -- асинхронный, то есть управление не ждет ответа сервера. Если нужно изменить вызов на синхронный (для использования реальных значений подмененного инстанса, например), то нужно указать параметр mode = "synchronous":

<xf:submission id="books6_xslttransformation" method="post"  mode = "synchronous"
               ref="instance('books6_mainInstance')" instance="books6_mainInstance" replace="instance"
  		action="secured/xslTransform?xsltfile=xformsxslttransformation_test.xsl">
</xf:submission>

JS процедуры для вызова GWT

  • gwtXFormSave -- процедура сохранения данных (процедура получает на вход ID Action и поэтому также может и фильтровать данные) и при необходимости обновления зависимых элементов.
  • gwtXFormUpdate -- процедура обновления зависимых элементов.
  • gwtXFormDownload -- процедура загрузки файлов с сервера
  • showSelector - процедура вызова селектора для дедуктивного выбора значения из списка.
  • showMultiSelector - процедура вызова селектора для множественного выбора значений из списка.


Процедура сохранения данных (gwtXFormSave)

Формат вызова gwtXFormSave:

 
gwtXFormSave('xformId', linkId,  Writer.toString(xforms.defaultModel.getInstanceDocument('mainInstance')), 'xformId_Save', true)

где

  • 'xformId' - константа,
  • linkId - ссылка на событие, которое нужно выполнить после сохранения данных. Если событие не нужно выполнять, задайте null.
  • 3-й параметр должен содержать данные формы, которые должны быть сохранены. В самом общем случае это содержимое главного инстанса формы, которое можно получить с помощью

такого кода: Writer.toString(xforms.defaultModel.getInstanceDocument('mainInstance'))

  • 4-й параметр содержит id кнопки для автоматической блокировки после нажатия на нее. Если параметр не задан, то все работает как раньше (никакой блокировки не происходит). Пример:
<xf:trigger>
	<xf:label>Отфильтровать</xf:label>
	<xf:action ev:event="DOMActivate">
		<xf:load
			resource="javascript:gwtXFormSave('xformId', '2',  Writer.toString(getSubformInstanceDocument('xformId_mainModel', 'xformId_mainInstance')))" />
	</xf:action>
</xf:trigger>
  • 5-й параметр - необходимость закрытия модального окна, если не задан linkId. true - нужно закрывать окно, false - не нужно.

Процедура обновления данных (gwtXFormUpdate)

Процедура выполняет обновление без сохранения данных или фильтрации!

Формат вызова gwtXFormUpdate:

 
gwtXFormUpdate('xformId', linkId,  Writer.toString(xforms.defaultModel.getInstanceDocument('mainInstance')), true)
  • В этом случае 3-й параметр используется для переопределения add_context у всех элементов и дочерних действий выполняемого действия.
  • 4-й параметр - необходимость закрытия модального окна, если не задан linkId. true - нужно закрывать окно, false - не нужно.

Процедура загрузки файлов с сервера (gwtXFormDownload)

Формат вызова gwtXFormDownload:

 
gwtXFormDownload('xformId', linkId,  Writer.toString(xforms.defaultModel.getInstanceDocument('mainInstance')))

где

  • 'xformId' - константа,
  • 'linkId' - ссылка на файл. Она должна совпадать с идентификатором id одной из процедур, объявленных в описании элемента XForms в файле информационной панели.

Процедура должна иметь тип "DOWNLOAD".

Процедура вызова селектора (showSelector)

Особенности процедуры showSelector:

  • Загрузка данных и общего количества значений может происходить либо одной, либо двумя процедурами (загрузка одной процедурой поддерживается начиная с версии 1.8.1.);
  • Присваивание значений по нажатию ОК может происходить либо при помощи процедуры, задаваемой в onSelectionComplete, либо с помощью XPath запросов;
  • Задавать currentValue можно либо текстовым значением, либо XPath запросом;
  • Задавать generalFilters можно либо одним текстовым значением (ранее), либо массивом XPath запросов:

Все способы создания селектора, кроме использования xf:selector с одной или двумя процедурами являются устаревшими.

Упрощенное задание селектора (xf:selector).

Начиная с версии 2.0.3 возможно упрощенное задание селекторов (через теги XForms):

		    <xf:selector 
		        buttonLabel       = "XPath(instance(quot(Showcase_Template_multiselector_simple_subform_myInstance))/infos/info/name)"  
		        buttonHint        = "Это хинт"
                        dataWidth           = "'600px'"
                        dataHeight          = "'450px'" 
                        visibleRecordCount  = "25"                    
                        manualSearch      = "true"
                        selectedFirst     = "true"	
		        procListAndCount  = "'[dbo].[regions_list_and_count]'"  
		        generalFilters    = "'фильтры'"
		        currentValue      = "'XPath(/schema/info[index(quot(securityRealEstateRow))]/city1)'"
		        startWith         = "false"
                        hideStartWith	  = "true"
		        windowCaption     = "'Выберите название'"
		        xpathMapping      = "{'XPath(/schema/info[index(quot(securityRealEstateRow))]/city1)' : 'name',  
'XPath(/schema/info[index(quot(securityRealEstateRow))]/city2)' : null}"
		        onSelectionCompleteAction =
		        "
					&lt;xf:action>
		  				 &lt;xf:message>Это вызов onSelectionCompleteAction&lt;/xf:message>
					&lt;/xf:action>
		        "
                        onSelectionComplete = "function(ok, selected){if (ok) {alert('Это вызов onSelectionComplete');}}"                        
		    />


Описание всех параметров функции showSelector:

  • buttonLabel - заголовок на кнопке вызова селектора задается атрибутом (если он не задан, то будет выведена надпись по умолчанию "Выбрать"), возможно задание в виде XPath запроса,
  • buttonHint - всплывающая подсказка при наведении курсора на кнопку,
  • dataWidth - ширина списка с данными,
  • dataHeight - высота списка с данными,
  • visibleRecordCount - количество записей, которые будут запрашиваться за один запрос с сервера,
  • manualSearch - признак того, что запрос на сервер должен выполняться в неавтоматическом режиме (по конпке),
  • selectedFirst - признак того, что после фильтрации в селекторе будет выделена первая запись,
  • procCount - имя процедуры для получения общего количества значений,
  • procList - имя процедуры для получения данных,
  • procListAndCount - имя процедуры для получения и данных, и общего количества значений,
  • generalFilters - фильтры, передаваемые в хранимую процедуру (может задаваться массивом XPath запросов),
  • currentValue - начальное значение строки поиска (может задаваться XPath запросом),
  • startWith - начальное значение галочки "Начинается с",
  • hideStartWith - нужно ли скрыть галочку "Начинается с" (при этом, поведение фильтрации записей определяется значением startWith),
  • windowCaption - заголовок окна селектора,
  • xpathMapping - задает связь между приемником и источником данных при помощи XPath-запросов (куда и откуда вставлять данные по нажатию ОК в селекторе),
  • onSelectionCompleteAction - задает xform'овский action, вызываемый при закрытии окна селектора, после того как выбранное значение передано в XForm'у,
  • onSelectionComplete - задает JS-функцию, вызываемую при закрытии окна селектора, после того как выбранное значение передано в XForm'у. При задании и onSelectionCompleteAction, и onSelectionComplete вначале выполняется onSelectionCompleteAction, затем onSelectionComplete.

При использовании xpathMapping XPath-запросы должны задаваться в виде XPath(...), кавычки в виде quot(...)!

При использовании generalFilters формируется XML-код вида:

<schema>
    <filter>
        <name id="1" value="qq2">
            <growth/>
            <eyescolour>green22</eyescolour>
            <music>kkkkkkkkk</music>
            <comment>dddddddddd</comment>
        </name>
        <name id="2" value="Николай"/>
        <name id="3" value="Геннадий"/>
        <name id="4" value="Дмитрий"/>
        <name id="5" value="Станислав"/>
        <name id="6" value="Петр"/>
    </filter>
    <filter>строка</filter>
    <filter>
        <eyescolour>Зеленый11</eyescolour>
        <eyescolour>Зеленый22</eyescolour>
    </filter>
</schema>

Компоненты селектора и соответствующие css-классы

Компонент селектора css-класс
кнопка в xform, вызывающая селектор server-selector-element
селектор server-selector-popup
заголовок селектора (пример) .server-selector-popup .dijitDialogTitleBar {background-color: red;}
картинка Закрыть в заголовке селектора (пример) .server-selector-popup .dijitDialogCloseIcon {background: url('../resources/internal/unselectAllImage.png') no-repeat center center;}
поле для поиска (фильтр) server-selector-searchstringtextbox-element
кнопка ручного поиска server-selector-manualsearchbutton-element
clear button server-selector-clearbutton-element
Starts With Checkbox server-selector-checkbox-element
грид со списком найденных элементов server-selector-listwrapper-element
ok button server-selector-okbutton-element
cancel button server-selector-cancelbutton-element

Мультиселектор (showMultiSelector)

Формат вызова showMultiSelector:

		    <xf:multiselector 
		        buttonLabel       = "New (Tag), MultiSelector, 1 процедура"  
                        dataWidth         = "'600px'"
                        dataHeight        = "'450px'"
                        selectedDataWidth = "'500px'"
                        visibleRecordCount = "'25'"                
                        manualSearch      = "true" 
		        selectedFirst     = "true"
		        procListAndCount  = "'[dbo].[regions_list_and_count]'"  
		        generalFilters    = "['XPath(instance(quot(myInstance))/infos/info[3]/name4)', 
'XPath(instance(quot(myInstance))/infos/info[3]/name4/@id)']"
		        currentValue      = "''"
		        windowCaption     = "'Выберите значения'"
		        needClear         = "true"
		        needInitSelection = "true"
		        xpathRoot         = "'XPath(instance(quot(myInstance))/infos)'"
		        xpathMapping      = "{'XPath(instance(quot(myInstance1))/info)':{'id': '@id','name':'name'}}"
		        onSelectionCompleteAction =
		        "
					&lt;xf:action>
		  				 &lt;xf:message>Это вызов onSelectionCompleteAction&lt;/xf:message>
					&lt;/xf:action>
		        "
                        onSelectionComplete = "function(ok, selected){if (ok) {alert('Это вызов onSelectionComplete');}}"                        
		    />

где

  • selectedDataWidth - ширина списка с выбранными данными;
  • needInitSelection - нужно ли загружать начальные выбранные значения при открытии селектора;
  • needClear - нужна ли очистка узла xpathRoot перед вставкой выбранных значений;
  • xpathRoot - задает узел, в который будут вставляться выбранные значения;
  • xpathMapping - сначала в элемент, указанный с помощью XPath запроса, помещаются поля выбранной записи, а затем этот элемент (origin) вставляется в xpathRoot;
  • buttonLabel - заголовок на кнопке вызова мультиселектора (если он не задан, то будет выведена надпись по умолчанию "Выбрать"), может задаваться XPath запросом;
  • onSelectionCompleteAction - задает xform'овский action, вызываемый при закрытии окна селектора, после того как выбранные значения переданы в XForm'у,
  • onSelectionComplete - задает JS-функцию, вызываемую при закрытии окна селектора, после того как выбранные значения переданы в XForm'у. При задании и onSelectionCompleteAction, и onSelectionComplete вначале выполняется onSelectionCompleteAction, затем onSelectionComplete.

Остальные параметры совпадают с соответствующими параметрами вызова селектора с единственным выбором.

Обратите внимание, упрощенно можно задавать любые варианты selector - даже самый старый, с использованием onSelectionComplete

Kомпоненты мультиселетора и соответсвующие css-классы

Компонент мультиселектора css-класс
кнопка в xform, вызывающая мультиселектор server-multiselector-element
мультиселектор server-multiselector-popup
заголовок мультиселектора (пример) .server-multiselector-popup .dijitDialogTitleBar {background-color: yellow;}
картинка Закрыть в заголовке мультиселектора (пример) .server-multiselector-popup .dijitDialogCloseIcon {background: url('../resources/internal/selectAllImage.png') no-repeat center center;}
поле для поиска (фильтр) server-multiselector-searchstringtextbox-element
кнопка ручного поиска server-multiselector-manualsearchbutton-element
clearbutton server-multiselector-clearbutton-element
Starts With Checkbox server-multiselector-checkbox-element
грид со списком найденных элементов server-multiselector-listwrapper-element
ok button server-multiselector-okbutton-element
cancel button server-multiselector-cancelbutton-element
select button server-multiselector-selectbutton-element
select all button server-multiselector-selectallbutton-element
unselect button server-multiselector-unselectbutton-element
unselect all button server-multiselector-unselectallbutton-element
поле для поиска выбранных элементов (фильтр 2) server-multiselector-searchselectedstringtextbox-element
find button server-multiselector-findbutton-element
грид со списком выбранных элементов server-multiselector-selectedlistwrapper-element


Триселектор (showTreeSelector)

Формат вызова триселектора с множественным выбором:

		    <xf:treeselector
		        buttonLabel        = "Вызов нового триселектора"
   		        windowCaption      = "'Выбор данных из дерева'"

   		        startWith          = "true"
                hideStartWith      = "false"
                manualSearch       = "false" 

                dataWidth          = "'500px'"
                dataHeight         = "'450px'"

				hideHeaders        = "false"

				checkParent = "true"

				checkChildren = "true"

				allowSelectAll = "true"

				columns =  "[
								{label: '', selector: 'checkbox', width: '20', resizable: false},							      
							    {field: 'name', label: 'Название', width: '200', renderExpando: true},								

								{field: 'column1', label: 'Столбец1', width: '100', horAlign: 'right'},										
								{field: 'column2', label: 'Ст2', width: '50', type: 'image', horAlign: 'center'}										
							]"

		        getDataProcName   = "'plugin/extJsTreeGetData.py'"                

		        generalFilters     = "['XPath(instance(quot(xformId_myInstance1))/item/name)']"

				needClear = "true"

		        needInitSelection = "true"

                xpathRoot         = "'XPath(instance(quot(xformId_myInstance))/items)'"
   			    xpathMapping      = "{'XPath(instance(quot(xformId_myInstance1))/item)':{'id': '@id','name':'name'}}"

   		        onSelectionComplete = "function(ok, selected){if (ok) {
     			        alert('Это вызов onSelectionComplete для триселектора');
		        }}"		        

		        onSelectionCompleteAction =
		        "
					&lt;xf:action>
		  				 &lt;xf:message>Это вызов onSelectionCompleteAction для триселектора&lt;/xf:message>
					&lt;/xf:action>
		        "
		    />


Формат вызова триселектора с единственным выбором:

		    <xf:treeselector
		        buttonLabel        = "Вызов нового триселектора без картинок на ноды"
   		        windowCaption      = "'Выбор данных из дерева'"

   		        startWith          = "true"
                hideStartWith      = "false"
                manualSearch       = "true" 

                dataWidth          = "'500px'"
                dataHeight         = "'450px'"

				hideHeaders        = "false"

				checkParent = "true"

				allowSelectAll = "false"

		        needInitSelection = "true"

				columns =  "[
								{label: '', selector: 'radio', width: '20', resizable: false},							      
							    {field: 'name', label: 'Название', width: '200', renderExpando: true},								

								{field: 'column1', label: 'Столбец1', width: '100', horAlign: 'right'},										
								{field: 'column2', label: 'Ст2', width: '50', type: 'image', horAlign: 'center'}										
							]"

		        getDataProcName   = "'plugin/extJsTreeGetData2.py'"                

		        generalFilters     = "['XPath(instance(quot(xformId_myInstance1))/item/name)']"

				needClear = "true"

                xpathMapping = "{'XPath(instance(quot(xformId_myInstance1))/item/name)' : 'name', 'XPath(instance(quot(xformId_myInstance1))/item/@id)' : 'id'}"		        

   		        onSelectionComplete = "function(ok, selected){if (ok) {
     			        alert('Это вызов onSelectionComplete для триселектора');
		        }}"		        

		        onSelectionCompleteAction =
		        "
					&lt;xf:action>
		  				 &lt;xf:message>Это вызов onSelectionCompleteAction для триселектора&lt;/xf:message>
					&lt;/xf:action>
		        "

		    />

где

  • hideHeaders - показывать ли заголовки грида;
  • checkParent - выделять ли родительские записи;
  • checkChildren - выделять ли детские записи;
  • allowSelectAll - возможность выделить все записи (чекбокс в заголовке столбца выделения или по Ctrl+A);
  • needInitSelection - нужно ли загружать начальные выбранные значения при открытии селектора;
  • columns - столбцы грида. Атрибуты:
    • selector - указывает на то, что столбец с данным атрибутом является столбцом выделения записей. Возможные значения checkbox/radio. Если checkbox, то в триселекторе реализуется множественный выбор записей. Если же radio, то выбор одной записи;
    • renderExpando - задает столбец, в котором реализуется работа с раскрытием/закрытием уровней;
    • field - поле данных, приходящих из серверной функции;
    • label - заголовок столбца;
    • width - ширина столбца;
    • horAlign - горизонтальное выравнивание столбца;
    • resizable - можно ли менять ширину столбца (по умолчанию, true);
    • type - тип столбца. В данный момент есть два типа: string (по умолчанию) и image;
  • getDataProcName - задает серверную функцию получения данных;

Остальные параметры совпадают с соответствующими параметрами вызова селектора с единственным выбором или мультиселектора.

Kомпоненты триселектора и соответствующие css-классы

Компонент триселектора css-класс
кнопка в xform, вызывающая триселектор server-treeselector-element
триселектор server-treeselector-popup
заголовок триселектора (пример) .server-treeselector-popup .dijitDialogTitleBar {background-color: yellow;}
картинка Закрыть в заголовке триселектора (пример) .server-treeselector-popup .dijitDialogCloseIcon {background: url('../resources/internal/selectAllImage.png') no-repeat center center;}
поле для поиска (фильтр) server-treeselector-searchstringtextbox-element
кнопка ручного поиска server-treeselector-manualsearchbutton-element
clearbutton server-treeselector-clearbutton-element
Starts With Checkbox server-treeselector-checkbox-element
грид со списком найденных элементов server-treeselector-listwrapper-element
ok button server-treeselector-okbutton-element
cancel button server-treeselector-cancelbutton-element


Пример серверной функции получения данных для триселектора

def getTreeSelectorData(context, main=None, add=None, filterinfo=None, session=None, 
                        params=None, curValue=None, startsWith=None, parentId=None):
    
    if parentId!=None:
        parentId=parentId+'.'
    else:
        parentId=''        

    if curValue!=None and curValue!='':
        curValue=' ['+curValue+']'
    else:
        curValue=''

    
    data = u'''
    <items>
        <item expand="true" id="'''+parentId+'''1" name="Название1 записи '''+parentId+'''1'''+curValue+'''" column1="Значение1" column2="solutions/default/resources/imagesingrid/test.jpg"  leaf="false" checked="false"  hideSelector = "true"                     treeGridNodeCloseIcon="solutions/default/resources/imagesingrid/TreeGridNodeClose.gif" treeGridNodeOpenIcon="solutions/default/resources/imagesingrid/TreeGridNodeOpen.gif" treeGridNodeLeafIcon="solutions/default/resources/imagesingrid/TreeGridLeaf.png"/>
        <item id="'''+parentId+'''2" name="Название2 записи '''+parentId+'''2'''+curValue+'''" column1="Значение2" column2="solutions/default/resources/imagesingrid/test.jpg"  leaf="false"      hideSelector = "false"                                  treeGridNodeCloseIcon="solutions/default/resources/imagesingrid/TreeGridNodeClose.gif" treeGridNodeOpenIcon="solutions/default/resources/imagesingrid/TreeGridNodeOpen.gif" treeGridNodeLeafIcon="solutions/default/resources/imagesingrid/TreeGridLeaf.png"/>
        <item id="'''+parentId+'''3" name="Название3 записи '''+parentId+'''3'''+curValue+'''" column1="Значение3" column2="solutions/default/resources/imagesingrid/test.jpg"                                hasChildren="false" treeGridNodeCloseIcon="solutions/default/resources/imagesingrid/TreeGridNodeClose.gif" treeGridNodeOpenIcon="solutions/default/resources/imagesingrid/TreeGridNodeOpen.gif" treeGridNodeLeafIcon="solutions/default/resources/imagesingrid/TreeGridLeaf.png"/>        
        <item id="'''+parentId+'''4" name="Название4 записи '''+parentId+'''4'''+curValue+'''" column1="Значение4" column2="solutions/default/resources/imagesingrid/test.jpg"               checked="true"   hasChildren="true"  treeGridNodeCloseIcon="solutions/default/resources/imagesingrid/TreeGridNodeClose.gif" treeGridNodeOpenIcon="solutions/default/resources/imagesingrid/TreeGridNodeOpen.gif" treeGridNodeLeafIcon="solutions/default/resources/imagesingrid/TreeGridLeaf.png"/>        
    </items>'''

#    context.message('dd11', u'Заголовок4', u"solutions/default/resources/group_icon_default.png");
#    context.warning('dd22');    
#    context.error('dd44', u'Заголовок4', u"solutions/default/resources/group_icon_default.png");
   
    res = JythonDTO(data)
   
    return res

Атрибуты записи:

  • id - идентификатор записи (для корректной работы должен быть уникальным по всем записям, не только в пределах одного уровня);
  • name - название записи;
  • column1 - значение атрибута column1 (аналогично, для других атрибутов);
  • checked - должна ли запись быть выделенной при отображении в гриде;
  • hasChildren - имеет ли запись child-записи;
  • hideSelector - скрывать ли контрол выбора записи (значение по умолчанию false);
  • expand - разворачивать ли запись;
  • treeGridNodeCloseIcon - tree-картинка свернутой записи;
  • treeGridNodeOpenIcon - tree-картинка развернутой записи;
  • treeGridNodeLeafIcon - tree-картинка записи без child-записей;

Хранимые процедуры

С элементом XForms могут быть связаны хранимые процедуры 8 типов:

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

CREATE PROC 
	@main_context varchar(MAX)='',
	@add_context varchar(MAX)='',
	@filterinfo xml='',
	@session_context xml='',
	@element_Id varchar(MAX)='',
	@data xml='' output,
        @settings xml='' output

Данная процедура может быть только одна (или ее может не быть вообще).

Структура xformsdata

Структура xml - любая, ее содержимое помещается в MainInstance.

Структура xformssettings

См. схему xformssettings.xsd

<properties>
	<action>
	default action
        </action>
	<event name="single_click" linkId="linkId">
		<Action>
                         ............................
                </Action>
	</event>
</properties>


2. Процедура для сохранения данных

CREATE PROC 
	@main_context varchar(MAX)='',
	@add_context varchar(MAX)='',
	@filterinfo xml='',
	@session_context xml='',
	@element_Id varchar(MAX)='',
	@data xml='',
        @error_mes varchar(MAX)='' output

где xformsdata содержит сохраняемые данные. error_mes - сообщение об ошибке в случае ее возникновения. В случае ошибки код возврата процедуры не должен быть равен 0 (код проверяется приложением).

Данная процедура может быть только одна (или ее может не быть вообще).

3. Процедура для запроса дополнительных данных через механизм submission

CREATE PROC 
	@inputdata xml='',
	@outputdata xml='' output,
        @error_mes varchar(MAX)='' output

где inputdata - входные данные, например, идентификатор инстанса, обновляемого посредством submission outputdata - содержимое инстанса, которое должно быть обновлено

Данных процедур может быть произвольное количество.

4. Процедура для загрузки файлов с сервера

CREATE PROCEDURE [dbo].[xforms_download1]
	@main_context varchar(MAX)='',
	@add_context varchar(MAX)='',
	@filterinfo xml='',
	@session_context xml='',
	@element_Id varchar(MAX)='',
	@xformsdata xml='',
	@filename varchar(64) output='',
	@file varbinary(MAX) output='',
	@error_mes varchar(MAX) output=''

где в filename нужно записать имя по умолчанию для загружаемого на компьютер клиента файла, а в file - содержимое файла. Остальные параметры по смыслу аналогичны одноименным параметрам в предыдущих процедурах.

Заголовок функции для celesta-скрипта. Celesta-скрипт должен вернуть объект JythonDownloadResult(file,filename), где file - поток с результирующим файлом, filename - имя скачиваемого файла.

def downloadBtzCard(context, main=None, add=None, filterInfo=None, session=None, elementId=None, xformsdata=None)


Данных процедур может быть произвольное количество, но все они должны быть описаны в файле информационной панели.

5. Процедура загрузки файлов на сервер

CREATE PROCEDURE [dbo].[xforms_upload1]
	@main_context varchar(MAX)='',
	@add_context varchar(MAX)='',
	@filterinfo xml='',
	@session_context xml='',
	@element_Id varchar(MAX)='',
	@xformsdata xml='',
	@filename varchar(64)='',
	@file varbinary(MAX)=null,	
	@error_mes varchar(MAX)='' output

где в filename передается имя загружаемого на сервер файла, а в file - содержимое файла. Остальные параметры по смыслу аналогичны одноименным параметрам в предыдущих процедурах. Данных процедур может быть произвольное количество, но все они должны быть описаны в файле информационной панели.

6,7,8. Компонента "селектор"

Внимание! Начиная с версии 2.0.1 в данные процедуры передается контекст (все 4 поля)

  • Шаблонная процедура получения общего числа записей для селектора:
CREATE PROCEDURE [dbo].[xxxcount](
   @main_context varchar(MAX)='',
   @add_context varchar(MAX)='',
   @filterinfo xml='',
   @session_context xml='',
   @params VARCHAR(MAX)='',
   @curValue VARCHAR(MAX)='',
   @startsWith BIT=0,
   @count INT=0 OUTPUT
   ) 
   AS
   BEGIN
      IF @startsWith = 1
         SET @curValue = @curValue + '%'
      ELSE
      SET @curValue = '%'+@curValue+'%';
 
      SELECT @count = COUNT(*) FROM [dbo].[Journal_XXX] WHERE [Journal_XXX_Name] LIKE @curValue; 
   END
  • Шаблонная процедура для получения данных для селектора:
CREATE  PROCEDURE [dbo].[xxxlist](
   @main_context varchar(MAX)='',
   @add_context varchar(MAX)='',
   @filterinfo xml='',
   @session_context xml='',
   @params VARCHAR(MAX)='',
   @curValue VARCHAR(MAX)='',
   @startsWith BIT=0,
   @firstRecord INT=1,
   @recordCount INT=20 
   ) 
   AS
   BEGIN
      IF @startsWith = 1
         SET @curValue = @curValue + '%'
      ELSE
         SET @curValue = '%'+@curValue+'%';
      WITH result AS 	(
         SELECT 
            [xxx_Id], 
            [xxx_Name],
            ROW_NUMBER() 
            OVER (ORDER BY [xxx_Name]) AS rnum 
         FROM [dbo].[xxx] WHERE [xxx_Name] LIKE @curValue)
      SELECT
         [xxx_Id] as [id], [xxx_Name] as [name] FROM result WHERE rnum BETWEEN (@firstRecord + 1) AND (@firstRecord + @recordCount)
         ORDER BY rnum;	
   END

При этом возвращаемый набор данных должен удовлетворять одному из 2 условий:

  1. иметь поля с именами id, name
  2. иметь 2 поля, первое из которых является id записи, а второе - именем записи (name).

Процедура может возвращать дополнительные поля. Они будут переданы в код xforms как параметры.

  • Шаблонная процедура для получения и данных, и общего числа записей для селектора:
CREATE  PROCEDURE [dbo].[xxxlist_and_count](
   @main_context varchar(MAX)='',
   @add_context varchar(MAX)='',
   @filterinfo xml='',
   @session_context xml='',
   @params VARCHAR(MAX),
   @curValue VARCHAR(MAX),
   @startsWith BIT,
   @firstRecord INT,
   @recordCount INT,
   @countAllRecords INT OUTPUT
   ) 
   AS
   BEGIN
      IF @startsWith = 1
         SET @curValue = @curValue + '%'
      ELSE
         SET @curValue = '%'+@curValue+'%';
         
      SELECT @countAllRecords = COUNT(*) FROM [dbo].[xxx] WHERE [xxx_Name] LIKE @curValue;          
         
      WITH result AS 	(
         SELECT 
            [xxx_Id], 
            [xxx_Name],
            ROW_NUMBER() 
            OVER (ORDER BY [xxx_Name]) AS rnum 
         FROM [dbo].[xxx] WHERE [xxx_Name] LIKE @curValue)
      SELECT
         [xxx_Id], [xxx_Name] 
         FROM result WHERE rnum BETWEEN (@firstRecord + 1) AND (@firstRecord + @recordCount)
         ORDER BY rnum;	
   END

В этой процедуре возвращаемый набор данных должен удовлетворять одному из 2 условий:

  1. иметь поля с именами id, name
  2. иметь 2 поля, первое из которых является id записи, а второе - именем записи (name).

Процедура может возвращать дополнительные поля. Они будут переданы в код xforms как параметры.

Настройка needReload

Предназначена для организации запрета выполнения действий на загрузку элемента XForms, расположенноuj на закладке, при выходе из модального окна с другим элементом XForms. Эта настройка содержится в служебном инстансе по пути /srvdata/schema/element/needReload. Настройка выставляется автоматически: при "обычной" загрузке элемента XForms (например, при первом открытии вкладки) needReload=true, а при выходе из модального окна needReload=false. Пример проверки значения:

            <xf:action ev:event="xforms-ready">
                <xf:action if="instance('srvdata')/element/needReload = 'true'">
                    <xf:message>Действие на загрузку</xf:message>
                </xf:action>
            </xf:action>

Переход на подформы

Внимание! Старые шаблоны xforms работать не будут. Для перевода в новый формат xforms можно воспользоваться скриптом XformsModifying.7z Для того чтобы обновить xforms необходимо сделать следующие щаги

  • открыть в eclipse файл xformsModifying.py
  • Заменить значение переменных
    • templDirPath - папка, где сейчас находятся формы, абсолютный путь
    • modDirPath - папка, куда будут помещены все исправленные файлы из папки templDirPath
  • Сохранить и запустить скрипт

Если modDirPath существует, то она предварительно будет удалена. В конце скрипта папки templDirPath и modDirPath будут идентичны по структуре, т.е. если xforms лежат в сложной папочной структуре, не стоит волноваться , все они будут переведены в новую версию с сохранением вложенности папок.

Терминология. То что раньше называлось X-формой, теперь называется подформой :)

В результате перехода на подформы стало возможно на одной странице показывать несколько подформ. В принципе, Алановский механизм позволяет несколько подформ показывать и в карточке, но тут потребуется доработка Showcase'а (плюс вопрос, а надо ли?). Также, теперь при открытии карточки с подформой не закрываются подформы на странице.

Модификация шаблонов xforms:

1. Все подформы имеют одну модель данных (то есть, все видят всех). Это особенность Алановского механизма подформ. С одной стороны, это позволяет из данной подформы обратиться к любой другой, что в некоторых случаях может быть удобно, а с другой, появляется необходимость уникального идентификатора подформы для

  1. id всех объектов подформы (модели, инстансов, сабмишенов итд)
  2. классов стилей подформы

В качестве такого идентификатора удобно использовать имя шаблона или же 'xformId'(в этом случае можно на одной странице использовать один и тот же шаблон с разными данными)

2.Необходимо явное указание инстанса в выражениях XPath. То есть, вместо

/schema/info/comment

нужно писать

instance('Showcase_Template_all_subform_mainInstance')/info/comment

3.При обращении к документу инстанса нужно вместо

"javascript:gwtXFormSave('xformId', '1',  Writer.toString(xforms.defaultModel.getInstanceDocument('mainInstance')))"

писать

"javascript:gwtXFormSave('xformId', '1',  Writer.toString(getSubformInstanceDocument('books5_mainModel', 
'books5_mainInstance')))"

где getSubformInstanceDocument(idModel, idInstance) - новая функция, которая по идентификаторам модели и инстанса возвращает документ инстанса

4.При обращении к модели нужно вместо

xforms.defaultModel.recalculate();
xforms.defaultModel.revalidate();
...

писать

getSubformModel('books5_mainModel').recalculate();
getSubformModel('books5_mainModel').revalidate();
...

где getSubformModel(idModel) - новая функция, которая по идентификатору модели возвращает саму модель

5.Вместо

<xf:action ev:event="xforms-ready">;
...

нужно писать

<xf:action ev:event="xforms-subform-ready">;
...

Пример такой модификации шаблона:

Было

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="xsltforms/xsltforms.xsl" type="text/xsl"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ev="http://www.w3.org/2001/xml-events"
	xmlns:xsd="http://www.w3.org/2001/XMLschema" xmlns:fs="http://www.curs.ru/ns/FormServer"
	xmlns:xf="http://www.w3.org/2002/xforms">
	<head>

        <style  type="text/css" >
            .nameInput
            .xforms-value {
            width: 200px !important;
            border: 0px;
            padding: 2px 0px 2px 0px; 
            background-color: yellow;
            margin: 2px 0px 0px 0px;
            min-height: 23px;
            }
        </style>	


		<!-- Простейшие контролы ввода и вывода -->
		<xf:model id="mainModel">
			<xf:instance id="mainInstance">
				<schema xmlns="">
					<info>
						<name />
						<growth />
						<eyescolour />
						<music />
						<comment />
					</info>
				</schema>
			</xf:instance>


			<xf:submission id="wrong_save" method="post" instance="mainInstance"
				replace="instance" ref="instance('mainInstance')" action="secured/submit?proc=xforms_submission11">
				<xf:action ev:event="xforms-submit-done">
					<xf:message>Submission успешно выполнен</xf:message>
				</xf:action>
				<xf:action if="event('response-body')!='null'" ev:event="xforms-submit-error">
					<xf:message>
						Ошибка при выполнении submission:
						<xf:output value="event('response-body')" />
					</xf:message>
				</xf:action>
			</xf:submission>

			<xf:submission id="good_save" method="post" instance="mainInstance"
				replace="instance" ref="instance('mainInstance')" action="secured/submit?proc=xforms_submission1">
				<xf:action ev:event="xforms-submit-done">
					<xf:message>Submission успешно выполнен</xf:message>
				</xf:action>
				<xf:action if="event('response-body')!='null'" ev:event="xforms-submit-error">
					<xf:message>
						Ошибка при выполнении submission:
						<xf:output value="event('response-body')" />
					</xf:message>
				</xf:action>
			</xf:submission>


			<xf:submission id="xslttransformation" method="post"
				instance="mainInstance" replace="instance" ref="instance('mainInstance')"
				action="secured/xslttransformer?xsltfile=xformsxslttransformation_test.xsl">
			</xf:submission>


			<xf:bind>
			</xf:bind>
		</xf:model>
	</head>
	<body>


		<div style="font-size: 15px;"> Имя: </div>
		<div>
			<xf:input class="nameInput" ref="/schema/info/name">
				<xf:help>Справка</xf:help>
				<xf:hint>Дополнительная информация</xf:hint>
			</xf:input>


			<xf:trigger>
				<xf:label>Selector</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:load
						resource="javascript:showSelector({
			           id : 'xformId',
                       procCount : '[dbo].[regionscount]',
                       procList  : '[dbo].[regionslist]',                       
                       generalFilters      : '',
                       currentValue        : '',
                       windowCaption       : 'Выберите название',
                       onSelectionComplete : function(ok, selected){
					if (ok) {
					var a = xforms.defaultModel.defaultInstance.doc.getElementsByTagName('info')[0].getElementsByTagName('name')[0];
					setValue(a, selected.name);
		 
					xforms.ready = false;
					xforms.refresh();
					xforms.ready = true;
							}
								   }});;" />
				</xf:action>
			</xf:trigger>

		</div>
		<div style="font-size: 15px;"> Цвет глаз (1): </div>
		<div>
			<xf:select1 appearance="full" ref="/schema/info/eyescolour">
				<xf:item>
					<xf:label>Голубой</xf:label>
					<xf:value>Голубой</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Карий</xf:label>
					<xf:value>Карий</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Зеленый</xf:label>
					<xf:value>Зеленый</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Серый</xf:label>
					<xf:value>Серый</xf:value>
				</xf:item>
			</xf:select1>
		</div>
		<div style="font-size: 15px;"> Цвет глаз (2): </div>
		<div>
			<xf:select1 ref="/schema/info/eyescolour">
				<xf:item>
					<xf:label>Голубой</xf:label>
					<xf:value>Голубой</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Карий</xf:label>
					<xf:value>Карий</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Зеленый</xf:label>
					<xf:value>Зеленый</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Серый</xf:label>
					<xf:value>Серый</xf:value>
				</xf:item>
			</xf:select1>
		</div>
		<div style="font-size: 15px;"> Любимая музыка (1): </div>
		<div>
			<xf:select appearance="full" ref="/schema/info/music">
				<xf:item>
					<xf:label>Классическая</xf:label>
					<xf:value>Классическая</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Инструментальная</xf:label>
					<xf:value>Инструментальная</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Эстрадная</xf:label>
					<xf:value>Эстрадная</xf:value>
				</xf:item>
			</xf:select>
		</div>
		<div style="font-size: 15px;"> Любимая музыка (2): </div>
		<div>
			<xf:select ref="/schema/info/music">
				<xf:item>
					<xf:label>Классическая</xf:label>
					<xf:value>Классическая</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Инструментальная</xf:label>
					<xf:value>Инструментальная</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Эстрадная</xf:label>
					<xf:value>Эстрадная</xf:value>
				</xf:item>
			</xf:select>
		</div>
		<div style="font-size: 15px;"> Комментарии: </div>
		<div>
			<xf:textarea ref="/schema/info/comment" />
		</div>
		<div style="clear: both;">
			<xf:output value="'Уважаемый '"/> 
			<xf:output value="/schema/info/name"/>  
            <xf:output value="'! Ваш рост: '"/>
            <xf:output value="/schema/info/growth"/>
            <xf:output value="', ваш цвет глаз: '"/>
            <xf:output value="/schema/info/eyescolour"/>
            <xf:output value="', ваш музыкальные предпочения: '"/>
            <xf:output value="/schema/info/music"/>

		</div>
		<div>

			<xf:trigger>
				<xf:label>Вызов XFormsSubmissionServlet</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:send submission="good_save" />
				</xf:action>
			</xf:trigger>

			<xf:trigger>
				<xf:label>Вызов XFormsSubmissionServlet с ошибкой</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:send submission="wrong_save" />
				</xf:action>
			</xf:trigger>

			<xf:trigger>
				<xf:label>Вызов XFormsTransformationServlet</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:send submission="xslttransformation" />
				</xf:action>
			</xf:trigger>

		</div>


		<div>

			<xf:trigger>
				<xf:label>Сохранить</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:load
						resource="javascript:gwtXFormSave('xformId', '1',  Writer.toString(xforms.defaultModel.getInstanceDocument('mainInstance')))" />
				</xf:action>
			</xf:trigger>

			<xf:trigger>
				<xf:label>Отфильтровать</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:load
						resource="javascript:gwtXFormFilter('xformId', '2',  Writer.toString(xforms.defaultModel.getInstanceDocument('mainInstance')))" />
				</xf:action>
			</xf:trigger>
			
			<xf:trigger>
				<xf:label>Обновить</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:load
						resource="javascript:gwtXFormUpdate('xformId', '3',  Writer.toString(xforms.defaultModel.getInstanceDocument('mainInstance')))" />
				</xf:action>
			</xf:trigger>

		</div>
	</body>
</html>

Стало

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ev="http://www.w3.org/2001/xml-events"
	xmlns:xsd="http://www.w3.org/2001/XMLschema" xmlns:fs="http://www.curs.ru/ns/FormServer"
	xmlns:xf="http://www.w3.org/2002/xforms">
	<head>
	
        <style  type="text/css" >
            .xformId_nameInput
            .xforms-value {
            width: 200px !important;
            border: 0px;
            padding: 2px 0px 2px 0px; 
            background-color: yellow;
            margin: 2px 0px 0px 0px;
            min-height: 23px;
            }
        </style>	
	
	
	
		<!-- Простейшие контролы ввода и вывода -->
		<xf:model id="xformId_mainModel">
			<xf:instance id="xformId_mainInstance">
				<schema xmlns="">
					<info>
						<name />
						<growth />
						<eyescolour />
						<music />
						<comment />
					</info>
				</schema>
			</xf:instance>
			
			<xf:instance id="xformId_myInstance">
				<schema xmlns="">
					<info>
						<name>Никодим</name>
						<growth />
						<eyescolour />
						<music />
						<comment />
					</info>
				</schema>
			</xf:instance>

			<xf:submission id="xformId_wrong_save" method="post" instance="xformId_mainInstance"
				replace="instance" ref="instance('xformId_mainInstance')" 
				action="secured/submit?proc=xforms_submission11">
				<xf:action ev:event="xforms-submit-done">
					<xf:message>Submission успешно выполнен</xf:message>
				</xf:action>
				<xf:action if="event('response-body')!='null'" ev:event="xforms-submit-error">
					<xf:message>
						Ошибка при выполнении submission:
						<xf:output value="event('response-body')" />
					</xf:message>
				</xf:action>
			</xf:submission>

			<xf:submission id="xformId_good_save" method="post" instance="xformId_mainInstance"
				replace="instance" ref="instance('xformId_mainInstance')" action="secured/submit?proc=xforms_submission1">
				<xf:action ev:event="xforms-submit-done">
					<xf:message>Submission успешно выполнен</xf:message>
				</xf:action>
				<xf:action if="event('response-body')!='null'" ev:event="xforms-submit-error">
					<xf:message>
						Ошибка при выполнении submission:
						<xf:output value="event('response-body')" />
					</xf:message>
				</xf:action>
			</xf:submission>


			<xf:submission id="xformId_xslttransformation" method="post"
ref="instance('xformId_mainInstance')"				instance="xformId_mainInstance" replace="instance" 
				action="secured/xslttransformer?xsltfile=xformsxslttransformation_test.xsl">
				<xf:action if="event('response-body')!='null'" ev:event="xforms-submit-error">
					<xf:message>
						Ошибка при выполнении submission:
						<xf:output value="event('response-body')" />
					</xf:message>
				</xf:action>
			</xf:submission>


			<xf:bind>
			</xf:bind>
		</xf:model>
	</head>
	<body>

		<h1>Шаблон xformId</h1>
		
		<div style="font-size: 15px;"> Имя: </div>
		<div>
			<xf:input class="xformId_nameInput" ref="instance('xformId_mainInstance')/info/name">
				<xf:help>Справка</xf:help>
				<xf:hint>Дополнительная информация</xf:hint>
			</xf:input>


			<xf:trigger>
				<xf:label>Selector</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:load
						resource="javascript:showSelector({
			           id : 'xformId',
                       procCount : '[dbo].[regionscount]',
                       procList  : '[dbo].[regionslist]',                       
                       generalFilters      : '',
                       currentValue        : '',
                       windowCaption       : 'Выберите название',
                       onSelectionComplete : function(ok, selected){
					if (ok) {
					var a = getSubformInstanceDocument('xformId_mainModel', 'xformId_mainInstance').getElementsByTagName('info')[0].getElementsByTagName('name')[0];
					setValue(a, selected.name);
		 
					xforms.ready = false;
					xforms.refresh();
					xforms.ready = true;
							}
								   }});" />
				</xf:action>
			</xf:trigger>			
			
			<xf:trigger>
				<xf:label>JS-функция с пробелом</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:load resource="javascript:{alert('g g');};" />
				</xf:action>
			</xf:trigger>			
			

		</div>
		
		<div style="font-size: 15px;"> Имя2: </div>
		<div>
			<xf:input class="xformId_nameInput" ref="instance('xformId_myInstance')/info/name">
				<xf:help>Справка</xf:help>
				<xf:hint>Дополнительная информация</xf:hint>
			</xf:input>
		</div>


		
		<div style="font-size: 15px;"> Цвет глаз (1): </div>
		<div>
			<xf:select1 appearance="full" ref="instance('xformId_mainInstance')/info/eyescolour">
				<xf:item>
					<xf:label>Голубой</xf:label>
					<xf:value>Голубой</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Карий</xf:label>
					<xf:value>Карий</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Зеленый</xf:label>
					<xf:value>Зеленый</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Серый</xf:label>
					<xf:value>Серый</xf:value>
				</xf:item>
			</xf:select1>
		</div>
		<div style="font-size: 15px;"> Цвет глаз (2): </div>
		<div>
			<xf:select1 ref="instance('xformId_mainInstance')/info/eyescolour">
				<xf:item>
					<xf:label>Голубой</xf:label>
					<xf:value>Голубой</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Карий</xf:label>
					<xf:value>Карий</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Зеленый</xf:label>
					<xf:value>Зеленый</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Серый</xf:label>
					<xf:value>Серый</xf:value>
				</xf:item>
			</xf:select1>
		</div>
		<div style="font-size: 15px;"> Любимая музыка (1): </div>
		<div>
			<xf:select appearance="full" ref="instance('xformId_mainInstance')/info/music">
				<xf:item>
					<xf:label>Классическая</xf:label>
					<xf:value>Классическая</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Инструментальная</xf:label>
					<xf:value>Инструментальная</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Эстрадная</xf:label>
					<xf:value>Эстрадная</xf:value>
				</xf:item>
			</xf:select>
		</div>
		<div style="font-size: 15px;"> Любимая музыка (2): </div>
		<div>
			<xf:select ref="instance('xformId_mainInstance')/info/music">
				<xf:item>
					<xf:label>Классическая</xf:label>
					<xf:value>Классическая</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Инструментальная</xf:label>
					<xf:value>Инструментальная</xf:value>
				</xf:item>
				<xf:item>
					<xf:label>Эстрадная</xf:label>
					<xf:value>Эстрадная</xf:value>
				</xf:item>
			</xf:select>
		</div>
		<div style="font-size: 15px;"> Комментарии: </div>
		<div>
			<xf:textarea ref="instance('xformId_mainInstance')/info/comment" />
		</div>
		<div style="clear: both;">
			<xf:output value="'Уважаемый '"/> 
			<xf:output value="instance('xformId_mainInstance')/info/name"/>  
            <xf:output value="'! Ваш рост: '"/>
            <xf:output value="instance('xformId_mainInstance')/info/growth"/>
            <xf:output value="', ваш цвет глаз: '"/>
            <xf:output value="instance('xformId_mainInstance')/info/eyescolour"/>
            <xf:output value="', ваш музыкальные предпочения: '"/>
            <xf:output value="instance('xformId_mainInstance')/info/music"/>

		</div>
		<div>

			<xf:trigger>
				<xf:label>Вызов XFormsSubmissionServlet</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:send submission="xformId_good_save" />
				</xf:action>
			</xf:trigger>

			<xf:trigger>
				<xf:label>Вызов XFormsSubmissionServlet с ошибкой</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:send submission="xformId_wrong_save" />
				</xf:action>
			</xf:trigger>

			<xf:trigger>
				<xf:label>Вызов XFormsTransformationServlet</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:send submission="xformId_xslttransformation" />
				</xf:action>
			</xf:trigger>

		</div>


		<div>
			<xf:trigger>
				<xf:label>Сохранить</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:load
						resource="javascript:gwtXFormSave('xformId', '1',  Writer.toString(getSubformInstanceDocument('xformId_mainModel', 'xformId_mainInstance')))" />
				</xf:action>
			</xf:trigger>
			<xf:trigger>
				<xf:label>Отфильтровать</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:load
						resource="javascript:gwtXFormFilter('xformId', '2',  Writer.toString(getSubformInstanceDocument('xformId_mainModel', 'xformId_mainInstance')))" />
				</xf:action>
			</xf:trigger>
			
			<xf:trigger>
				<xf:label>Обновить</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:load
			            resource="javascript:gwtXFormUpdate('xformId', '3',  Writer.toString(getSubformInstanceDocument('xformId_mainModel', 'xformId_mainInstance')))" />
				</xf:action>
			</xf:trigger>
		</div>

	</body>
</html>

Здесь в качестве уникального идентификатора подформы используется 'xformId'.

4.Замечание. Серверный инстанс "srvdata" теперь называется "уникальный_идентификатор_подформы_srvdata". В данном примере, "xformId_srvdata".

5.Важное замечание! Для того, чтобы работала загрузка файлов на сервер (uploader) необходимо, чтобы в качестве уникального идентификатора подформы использовался xformId.

Компонента задания цвета (jscolor)

В Showcase появилась компонента для задания цвета. Пример использования

		<div  style="width: 15px; padding: 21px 0px 0px 0px; float: left; ">
			<xf:trigger class="aid-button">
				<xf:label>X</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:load
						resource="javascript:var
 myPicker = new jscolor.color(document.getElementById('myField1'), {});	myPicker.fromString('99FF33')"
						></xf:load>
				</xf:action>
			</xf:trigger>
		</div>
		<input class="colorPaletteOfShowcase" value="66ff00" id="myField1"/>

Замечание. Компонента пока не интегрирована в XForms, то есть нельзя задавать через теги XForms

Компонента "триселектор"

Предназначена для выбора записей из списка (по аналогии с селектором и мультиселектором), в случае, если список имеет древовидную структуру.

Компонента задается как

			<xf:trigger>
				<xf:label>Вызов ExtJsTree плагина</xf:label>
				<xf:action ev:event="DOMActivate">
					<xf:load
						resource="javascript:gwtCreatePlugin({
							id:'xformId',
							plugin:'extJsTree',
							proc:'plugin/extJsTree.py',
							postProcessProc:'plugin/handleExtJsTree.py',
							getDataProcName:'plugin/extJsTreeGetData.py',
							generalFilters:['XPath(instance(quot(xformId_myInstance1))/item/name)'],							
							params:{
								treePanel: {
									title: 'Элементы дерева',
									listeners: {
										checkchange: {
											fn: function(node, checked, eOpts) {
												if (checked) {
													alert('CheckChange event. Select node '+node.get('name')+'(id:'+node.get('id')+', attr1:'+node.get('attr1')+')');
												}
											}
										}
									}
								},
								core: {
									checkParent:true,
									filter:{
										startsWith:true,
										delay:900
									}
								},
								
								
								dataModel:{
									fields:[
										{name: 'attr1', type: 'string'},
										
										{name: 'column1', type: 'string'},										
										{name: 'column2', type: 'string'}										
									]
								},

								view:{
									columns:[
										{ header: 'Столбец1', dataIndex: 'column1'},
										{ header: 'Столбец2', dataIndex: 'column2'}
									]
								}
								
								
							},
							options: {	
                                                                needInitSelection: true,							
								dataWidth: '500px',
								dataHeight: '450px',
								windowCaption: 'Выбор данных из дерева',
								onSelectionComplete: function(ok, plugin) {
									if (ok) {
										plugin.utils.singleXpathMapping({
											'XPath(instance(quot(xformId_myInstance1))/item/name)':'name',
											'XPath(instance(quot(xformId_myInstance1))/item/@id)':'id'
										});
										plugin.utils.multiXpathMapping({
											xpathRoot:'XPath(instance(quot(xformId_myInstance))/items)',
											xpathMapping:{'XPath(instance(quot(xformId_myInstance1))/item)':{'id': '@id','name':'name'}}
										}, true);
									}
								}
							}
						});" />
				</xf:action>
			</xf:trigger>

Описание параметров:

  • id:'xformId' - идентификатор xforms,
  • plugin:'extJsTree' - название плагина для поддержки триселектора,
  • proc:'plugin/extJsTree.py' - название той процедуры которая должна вернуть нам данные в виде XML. Данная процедура срабатывает первой при вызове tree selector’a
# coding: utf-8
'''
Created on 17.12.2011

@author: bogatov
'''
from ru.curs.showcase.core.jython import JythonProc
from ru.curs.showcase.core.jython import JythonDTO

# init vars
main = ""
add = ""
session = ""
filterContext = ""
elementId = ""
xmlParams = ""


class extJsTree(JythonProc):
    def getPluginRawData(self, context, elId, params):
        global main, add, session, filterContext, elementId, xmlParams
        main = context.getMain()
        if context.getAdditional():
            add = context.getAdditional()
        session = context.getSession()
        if context.getFilter():
            filterContext = context.getFilter()
        elementId = elId
        xmlParams = params
        return mainproc()


def mainproc():
    data = u'''
    <items>
			<item id="1" name="Lazy load item" leaf="false" checked="false"/>
			<item id="2" name="Расходование денежных средств"                          column1="Значение2"  column2="solutions/default/resources/imagesingrid/test.jpg" cls="folder">
				<children>
					<item id="21" name="Оплата поставщикам за товар" leaf="true"       column1="Значение21" column2="solutions/default/resources/imagesingrid/TreeGridLeaf.png"  attr1="a" checked="false"/>
					<item id="22" name="Расходы по таможенному оформлению" leaf="true" column1="Значение22" column2="solutions/default/resources/imagesingrid/test.jpg"  attr1="b" checked="false"/>
					<item id="23" name="Расходы  на аренду, коммунальные услуги"       column1="Значение3"  column2="solutions/default/resources/imagesingrid/TreeGridLeaf.png"                    cls="folder">
						<children>
							<item id="231" name="Аренда недвижимости" leaf="true" attr1="c" checked="false"/>
							<item id="232" name="Коммунальные услуги" leaf="true" attr1="d" checked="false"/>
							<item id="233" name="Расходы на содержание сооружений и оборудования" leaf="true" attr1="e" checked="false"/>
						</children>
					</item>
					<item id="24" name="Расходы на персонал" cls="folder">
						<children>
							<item id="241" name="Расходы на оплату труда" leaf="true" attr1="f" checked="false"/>
							<item id="242" name="Страхование персонала, мед.услуги" leaf="true" attr1="g" checked="false"/>
							<item id="243" name="Матпомощь, подарки" leaf="true" attr1="m" checked="false"/>
						</children>
					</item>
					<item id="25" name="Услуги связи" leaf="true" attr1="k" checked="false"/>
					<item id="26" name="Маркетинг и реклама" leaf="true" attr1="l" checked="false"/>
					<item id="27" name="Обеспечение безопасности" leaf="true" attr1="n" checked="false"/>
				</children>   
			</item>
			<item id="3" name="Поступление денежных средств" cls="folder">
				<children>
					<item id="31" name="Доход от продажи товара" leaf="true" attr1="o" checked="false"/>
					<item id="32" name="Расходы по таможенному оформлению" leaf="true" attr1="p" checked="false"/>
					<item id="33" name="Возврат денежных средств от контрагентов" leaf="true" attr1="r" checked="false"/>				
					<item id="34" name="Проценты по депозитам" leaf="true" attr1="s" checked="false"/>
				</children>   
			</item>
    </items>'''
    settings = u'''
    <properties>
    </properties>
    '''
    res = JythonDTO(data, settings)
    return res

if __name__ == "__main__":
    mainproc()


  • getDataProcName:'plugin/extJsTreeGetData.py' - указывается процедура которая так же возвращает нам данные в виде XML. Отличие этой процедуры от процедуры указанной в «proc» в том что она вызывается после совершение каких либо действий в окне tree selector’a (раскрытие дерева, вод значения в поисковую строку и т.д.) . Процедуру в «proc» можно не указывать, тогда при вызове tree selector’a сразу отработает процедура указанная в «getDataProcName»
# coding: utf-8
'''
Created on 15.02.2013

@author: bogatov
'''
from ru.curs.showcase.core.jython import JythonProc
from ru.curs.showcase.core.jython import JythonDTO
import xml.etree.ElementTree as ET

# init vars
main = ""
add = ""
session = ""
filterContext = ""


class extJsTreeGetData(JythonProc):
    def getPluginData(self, context, attr):
        global main, add, session, filterContext, elementId
        main = context.getMain()
        if context.getAdditional():
            add = context.getAdditional()
        session = context.getSession()
        if context.getFilter():
            filterContext = context.getFilter()
        return mainproc(attr)


def mainproc(attributes):
    parentId=''
    curValue=''
    xmlParams = attributes.getXmlParams()
    if xmlParams!=None:
        root = ET.fromstring(xmlParams)        
        pId = root.find('.//id')
        if pId!=None and pId.text!=None:
            parentId=pId.text+'.'
        pCurValue = root.find('.//curValue')
        if pCurValue!=None and pCurValue.text!=None:
            curValue=' ['+pCurValue.text+']'
    data = u'''
    <items>
		<item id="'''+parentId+'''1" name="Lazy loaded item '''+parentId+'''1'''+curValue+'''" leaf="false" checked="false"/>
		<item id="'''+parentId+'''2" name="Lazy loaded item '''+parentId+'''2'''+curValue+'''" leaf="false"/>
    </items>'''
    res = JythonDTO(data)
    return res

if __name__ == "__main__":
    mainproc()


  • postProcessProc:'plugin/handleExtJsTree.py' - указывается питон процедура, которая превращает пришедший ей на вход XML (из процедур указанных в «proc», «getDataProcName») в JSON. Данный код стандартен, и требует изменений, если необходимо вводить новые атрибуты.
# coding: utf-8
'''
Created on 17.12.2011

@author: bogatov
'''
from ru.curs.showcase.core.jython import JythonProc, JythonDTO
from ru.curs.showcase.util.xml import XMLUtils
from org.xml.sax.helpers import DefaultHandler
from ru.curs.showcase.util import TextUtils

# init vars
data = '''
    <item text="item1" cls="folder">
        <children>
            <item text="item 1.1" leaf="true" checked="false"/>
        </children>
    </item>'''
result = u''


class myHandler(DefaultHandler):
    isFirst = 1
    def startElement(self, namespaceURI, lname, qname, attrs):
        global result
        if (qname == "items"):
            result += u'['
        elif (qname == "item"):
            if (myHandler.isFirst != 1):
                result += u','
            result += u'{'            
            if (attrs.getIndex("id") > -1):
                result += u"id: '" + attrs.getValue('id') + "'"
            if (attrs.getIndex("name") > -1):
                result += u", name: '" + attrs.getValue('name') + "'"            
            if (attrs.getIndex("cls") > -1):
                result += u", cls: '" + attrs.getValue('cls') + "'"
            if (attrs.getIndex("expanded") > -1):
                result += u", expanded: " + attrs.getValue('expanded')
            if (attrs.getIndex("leaf") > -1):
                result += u", leaf: " + attrs.getValue('leaf')
            if (attrs.getIndex("checked") > -1):
                result += u", checked: " + attrs.getValue('checked')
            if (attrs.getIndex("attr1") > -1):
                result += u", attr1: '" + attrs.getValue('attr1') + "'"
                
            if (attrs.getIndex("column1") > -1):
                result += u", column1: '" + attrs.getValue('column1') + "'"
            if (attrs.getIndex("column2") > -1):
                result += u", column2: '<a><img border=\"0\" src="+attrs.getValue('column2')+"></a>'"
                     
        elif (qname == "children"):
            result += u", children: ["
            myHandler.isFirst = 1
    def endElement(self, namespaceURI, lname, qname):
        global result
        if (qname == "items"):
            result += u']'
        elif (qname == "item"):
            result += u'}'            
            myHandler.isFirst = 0
        elif (qname == "children"):
            result += u"]"

class handleExtJsTree(JythonProc):

    def postProcess(self, context, elId, adata):
        global data
        data = adata
        return mainproc()


def mainproc():	
    global result
    parser = XMLUtils.createSAXParser()
    stream = TextUtils.stringToStream(data)
    parser.parse(stream, myHandler())
    return JythonDTO([result])

if __name__ == "__main__":
    from org.python.core import codecs
    codecs.setDefaultEncoding('utf-8')
    mainproc()
    • для отображения картинки необходимо преобразование вида
            if (attrs.getIndex("column2") > -1):
                result += u", column2: '<a><img border=\"0\" src="+attrs.getValue('column2')+"></a>'"


  • generalFilters:['XPath(instance(quot(xformId_myInstance1))/item/name)'] - указывается условия которые необходимо передать процедурам указанным в «proc» и в «getDataProcName». (Аналогмчно селектору),
  • params - параметры, передаваемые в компоненту. Для удобства разбиты на несколько частей:
    • treePanel - предназначенные для ExtJs компоненты Ext.tree.Panel т.е. в данном параметре можно переопределять любые настройки Ext.tree.Panel
								treePanel: {
									title: 'Элементы дерева',
									listeners: {
										checkchange: {
											fn: function(node, checked, eOpts) {
												if (checked) {
													alert('CheckChange event. Select node '+node.get('name')+'(id:'+node.get('id')+', attr1:'+node.get('attr1')+')');
												}
											}
										}
									}
								},


    • core - дополнительные параметры передаваемые в плагин
								core: {
									checkParent:true,
									filter:{
										startsWith:true,
										delay:900,
										autofilter:true
									}
								},
      • checkParent - если true, то выделение данной записи приводит к автоматическому выделению родительской записи,
      • startsWith - начальное состояние чекбокса "Начинается с",
      • delay - время задержки отправки запроса на сервер (необходимо для исключения ненужных запросов пока пользователь вводит символы),
      • autofilter - режим отправки запросов на сервер. Если true - то запросы отправляются автоматически по мере набирания символов (с учетом параметра delay), если false - то по нажатию пользователем кнопки "Найти"


    • dataModel - модель данных, используемая при построении дерева
								dataModel:{
									fields:[
										{name: 'attr1', type: 'string'},
										
										{name: 'column1', type: 'string'},										
										{name: 'column2', type: 'string'}										
									]
								},


    • view -- задает столбцы, которые будут отображаться в дереве
								view:{
									columns:[
										{ header: 'Столбец1', dataIndex: 'column1'},
										{ header: 'Столбец2', dataIndex: 'column2'}
									]
								}
      • dataIndex - название поля из Модели данных


  • options - опции построения плагина (аналогичны соответствующим настройкам в селеторе и мультиселекторе)
							options: {	
                                                                needInitSelection: true,							
								dataWidth: '500px',
								dataHeight: '450px',
								windowCaption: 'Выбор данных из дерева',
								onSelectionComplete: function(ok, plugin) {
									if (ok) {
										plugin.utils.singleXpathMapping({
											'XPath(instance(quot(xformId_myInstance1))/item/name)':'name',
											'XPath(instance(quot(xformId_myInstance1))/item/@id)':'id'
										});
										plugin.utils.multiXpathMapping({
											xpathRoot:'XPath(instance(quot(xformId_myInstance))/items)',
											xpathMapping:{'XPath(instance(quot(xformId_myInstance1))/item)':{'id': '@id','name':'name'}}
										}, true);
									}
								}
							}

Cборка шаблонов xForm из частей

В версии стабильного релиза платформы 4.0.0 добавлен функционал сборки шаблонов xForm из частей. Это введено для упрощения работы разработчиков по написанию одинакового кода в разных шаблонах форм в Showcase.

Мотивация для введения данного функционала следующая. Часто возникает ситуация, при которой в рамках одной перспективы (юзердаты) шаблоны для генерации showcase-элемента данных xForm очень похожи, практически копируют друг друга за исключением отдельных выделенных частей в описании шаблонов и/или биндах. Может возникнуть ситуация, когда таких форм в рамках одной перспективы много. Чтобы при необходимости внесения изменений в шаблоны не приходилось менять вручную шаблоны всех форм одновременно (что долго и затратно), предлагается эти куски-вставки для шаблона формы вынести в отдельный файл xml, скрипт python или celesta (в соответствии с принципом универсальных источников Showcase). Таким образом, в основной шаблон будут вставляться куски-вставки, полученные из xml, celesta или python в процессе генерации шаблона формы на сервере в режиме runtime. Эти вставки можно будет генерировать на основе текущего контекста при загрузке формы (используя скрипты celesta или python). Это отменит необходимость внесения изменений во все похожие шаблоны форм, которые используются в данной перспективе. Теперь при использовании механизма сборки шаблонов из частей потребуется изменения только этого выделенного куска шаблона/биндов в выделенном файле xml, скриптах celesta или python.

Итак, для того, чтобы можно было вставлять в шаблон (template) элемента xForm сформированные отдельно куски, используемые в нескольких элементах xFoms одновременно, необходимо:

  • Добавить в описании элемента xForm в информационной панели атрибут buildTemplate, отвечающий за сборку шаблона. Например:
<element id="xform_id" type="xforms" template="myXformMainTemplate.xml" proc="myXformDataProc" neverShowInPanel="true" buildTemplate="true"/>

Если атрибут присутствует в описании элемента и принимает значение "true", включается механизм сборки шаблона формы из частей. (Этот атрибут по умолчанию установлен в true, и эта вставка необязательна. Это сделано для удобства разработчиков решения: подразумевается, что если в основном шаблоне включена вставка части шаблона, то разработчик уже этим предполагает необходимость установки в buildTemplate="true".) НО! Если этот атрибут установлен в false, то данный механизм не включается.

  • В текст основного шаблона (например, myXformMainTemplate.xml) в соответствующем месте, куда вы хотите вставить кусок шаблона, должна находиться вставка вида
<div insertTemplate="partTemplate.xml"></div>

и/или

<div insertBind="bindTemplate.xml"></div>

где insertTemplate - означает источник данных для куска шаблона, а insertBind - источник данных для вставки в бинды. Источники данных для кусков-вставок задаются в соответствии с принципом универсальных источников. Поддерживаются следующие источники данных: файл xml, скрипт celesta и python. При запуске данных скриптов в них приходят все контексты, как и для построения шаблонов.

Например, для вставки части шаблона, в основном шаблоне необходимо задать:

<xf:group ref="instance('xformId_mainInstance')/blahblahblah">
  <div insertTemplate="partTemplate.xml"></div>
</xf:group>

Так как в шаблоне myXformMainTemplate.xml (см. пример выше) встречается div с атрибутами insertTemplate, то вместо тега div основного шаблона вставится "шаблон-вставка" из файла partTemplate.xml (данный файл должен находиться в папке xforms текущей перспективы). Как уже было сказано, кроме формата xml для данной вставки могут использоваться скрипты python или celesta. В случае python атрибут insertTemplate должен иметь значение insertTemplate="partTemplate.py", а соответствующий файл лежать в папке scripts\jython текущей перспективы. При использовании celesta атрибут insertTemplate должен иметь значение insertTemplate="grain.partTemplate.template.cl" , где grain - имя гранулы, в которой располагается класс-файл partTemplate.py, а template - имя функции, задающей часть шаблона. Папка гранулы должна лежать в папке score перспективы.

Аналогично этот функционал работает и при вставки биндов (используйте атрибут insertBind). Теги <div/> для вставки биндов могут находится в любом месте внутри тега <body> основного шаблона. При этом вставка содержимого файла bindTemplate.xml происходит в тег <xf:model> после всего, что в нём находится.

Обратите внимание: куски-вставки шаблонов и биндов, получаемые из файла xml, скриптов celesta или python должны в обязательном порядке содержать корневой тег <partOfXFormTemplate> c указанием пространств имён (что в принципе необязательно).

Ещё замечание: Не стоит беспокоиться об удалении тегов <div/> с атрибутами insertTemplate и insertBind. Данные теги будут автоматически удалены из шаблона, который придет на вход showcase-элементу xForm.