Грид (grid, LiveGrid, TreeGrid, PageGrid) в Showcase

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

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

Содержание

Описание

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

CREATE PROC 
    @main_context varchar(MAX)='',
    @add_context varchar(MAX)='',
    @filterinfo xml='',
    @session_context xml ='',    
    @element_id varchar(MAX) ='',
    @sortcols varchar(MAX) ='',        
    @settings xml='' output,
    @error_mes varchar(MAX)='' output

В случае загрузки грида в два этапа шаблон процедуры с настройками следующий:

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

Вторая процедура возвращает только DataSet.

CREATE PROC 
	@main_context varchar(MAX)='',
	@add_context varchar(MAX)='',
	@filterinfo xml='',
        @session_context xml='',
        @element_Id varchar(MAX)='',
        @sortcols varchar(MAX) ='',        
        @firstrecord int=1,
        @pagesize int=20

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

Рекомендуется, чтобы названия обеих процедур - для данных и метаданных - имели общий "корень".

Параметр forceLoadSettings

ВАЖНО: при использовании 2 процедур для загрузки грида увеличивается скорость загрузки, но возникает побочный эффект, о котором следует помнить. В данном режиме грид запоминает число записей, которое было в таблице на момент первой отрисовки грида и в дальнейшем использует это число для подсчета числа страниц в гриде. Поэтому если в грид были добавлены новые записи, то их видимость зависит от выбранного размера страницы. Чтобы гарантировано увидеть все записи нужно перегрузить грид из навигатора. Внимание! В настройки грида добавлен параметр forceLoadSettings. Если forceLoadSettings="true", то настройки грузятся каждый раз вместе с данными и, соответственно, грузится правильное количество записей. Т.о. можно убрать данный побочный эффект. (например, <properties flip="false" forceLoadSettings="true" pagesize="10" totalCount="'||"totalCount"||'" autoSelectRecordId="1" profile="gr_withoutExcel.properties"/>)

Аналогичная ситуация и с labels (header, footer) грида. В частности, это означает, что для корректного отображения labels в режиме восстановления пользовательских настроек (keep_user_settings="true") необходимо выставить forceLoadSettings="true"

Структура gridsettings

См. схему gridsettings.xsd и gridProperties.xsd

<gridsettings>
<!-- Тут описываются кнопки фильтров, которые находятся над гридом. Только фильтр по тексту. Не реализовано-->
<filters>
    <filter id="код фильтра" caption="наименование фильтра" type="text" query="SQL-запрос для фильтрации"/>
    <filter ...>
</filters>
<labels>
    <header>некий html-текст</header>
    <footer>некий html-текст</footer>
</labels>
<columns>
<!-- precision - число знаков после запятой-->
	<col id="имя столбца" width="20px" precision="2" type="IMAGE | LINK | DOWNLOAD" horAlign="LEFT | CENTER | RIGHT" firstSortDirection="ASC | DESC" visible="false"/>
	...
</columns>
<action>
	default action
</action>

<properties flip="false" pagesize="10" autoSelectColumnId="firstcol" 

autoSelectRecordId="1"  autoSelectRelativeRecord="true" 
autoSelectOffset="30" autoSelectRecordUID="85CD7E79-F23E-454B-B4F5-E81EC3755D99"

profile="xxx.properties"  totalCount="20" fireGeneralAndConcreteEvents="true"

expandAllRecords="true"  

/>

</gridsettings>
  • autoSelectColumnId - ид столбца, который выделяется при открытии по умолчанию. При этом выполняются события single_click, этого столбца
  • autoSelectRecordId - номер записи (не идентификатор!), которая выделяется при открытии по умолчанию. При этом выполняются события single_click для этой записи.
  • autoSelectRelativeRecord - нумерация строк относительно страницы или абсолютная. Используется для autoSelectRecordId. По умолчанию имеет значение true.


  • autoSelectRecordUID - идентификатор записи, которую необходимо выделить (если не задан, то учитывается autoSelectRecordId),
  • autoSelectOffset - начальное смещение (аналог начальной страницы, но с перспективой в дальнейшем использовать и для live-грида).

При этом, открывается начальная страница грида, определяемая autoSelectOffset, после чего на ней выделяется запись с идентификатором autoSelectRecordUID. Если autoSelectOffset не задан, то запись ищется на первой странице, если autoSelectRecordUID не задан, то просто открывается нужная страница. Предполагается, что вместе с идентификатором выделенной записи будет запоминаться и autoSelectOffset. Его можно взять из sessioncontext \ related \ gridContext \ liveInfo \ offset

<sessioncontext>
 <username>master</username>
 <sid/>
 <login>master</login>
 <sessionid>E8069115B6293FEC33C5E61837FDC780</sessionid>
 <email/>
 <fullusername>master</fullusername>
 <phone/>
 <ip>0:0:0:0:0:0:0:1</ip>
 <urlparams>
  <urlparam name="qq1" value="[ww1]"/>
  <urlparam name="qq2" value="[ww2]"/>
 </urlparams>
 <userdata>default</userdata>
 <related>
  <gridContext id="2">
   <partialUpdate>false</partialUpdate>
   <pageInfo number="1" size="20"/>
   <liveInfo limit="10" offset="30" pageNumber="1" totalCount="0"/>
   <gridFilterInfo/>
   <currentRecordId>Мурманская обл.</currentRecordId>
   <currentDatapanelWidth>1376</currentDatapanelWidth>
   <currentDatapanelHeight>997</currentDatapanelHeight>
   <currentColumnId>Регион</currentColumnId>
   <selectedRecordId>Мурманская обл.</selectedRecordId>
  </gridContext>
 </related>
</sessioncontext>

Функционал, связанный с autoSelectRecordUID и autoSelectOffset, доступен только для dgrid'ов.


  • pagesize - количество строк на страницу.
  • profile - имя профайла настроек грида (описание профайла настроек грида смотрите ниже в разделе "Описание профайлов настроек грида").
  • totalСount - количество строк в таблице. Атрибут обязательный для заполнения. Для общности атрибут сделан обязательным и в случае загрузки данных и метаданных одной процедурой. В этом случае можно просто возвращать 0.
  • fireGeneralAndConcreteEvents - указание на то, что нужно выполнять общее событие для строки и конкретное для ячейки при клике в ячейку (при наличии обоих событий, естественно). События выполняются асинхронно, но первым начинает выполняться событие для строки,
  • expandAllRecords - указание на то, что при первом открытии tree-грида будут раскрыты все записи (пока актуально только для tree-грида). Внимание! Данный сценарий может потребовать много времени для открытия грида. Использовать с осторожностью! (значение по умолчанию равно false).
  • Для столбцов можно также задать ширину (width). Данное значение переопределяет значение из профайла настроек грида. В режиме отображения SINGLELINE и MULTILINE данное значение интерпретируется как реальная ширина столбцов, а в режиме AUTOFIT - как минимальная ширина столбца.
  • Для числовых столбцов можно также задать число знаков после запятой (precision).
  • Для всех столбцов с типом IMAGE, LINK или DOWNLOAD обязательно должна быть запись в <columns> с заполненным атрибутом type. Для других типов столбцов значение type указывать не нужно - тип будет определен по типу столбца SQL!
  • Для столбцов можно задать признак видимости в гриде (visible). Если visible="false", то столбец в гриде не отображается, но экспортируется в excel. По умолчанию, visible="true". (Данная настройка работает только для JS-GRID'ов)
  • horAlign - задает вырвнивание данных в столбце.
  • firstSortDirection - задает направление сортировки при первом клике на столбце (по умолчанию, ASC).

Столбцы IMAGE

Для столбцов типа IMAGE в таблице должен быть указан путь к картинке относительно каталога images.in.grid.dir из app.properties. Поддерживаются все форматы картинок, допустимые для тэга <img>.

Столбцы LINK

Примеры задания значений для столбцов типа LINK:

Ссылка с картинкой, которая хранится у вас и открывается в том же окне:

<link href="http:''localhost:8080/rsS" image="${images.in.grid.dir}/rsS.gif" text="Ространс" />

где

  • ${images.in.grid.dir} - см. раздел "Переменные HTML".
  • text - подпись к картинке, которая будет записана в тэг <alt> в HTML-коде.

Ссылка с картинкой, которая хранится в сети и открывается в новом окне:

<link href="http:''rbc.ru" image="http:''rbc.ru/rbc.gif" text="rbc.ru" openInNewTab="true"/>

Ссылка без картинки:

<link href="http:''rbc.ru" text="rbc.ru" openInNewTab="true"/>

где text - это текст, отображаемый в браузере для ссылки.

Ссылка без картинки и с подписью по умолчанию:

<link href="http:''rbc.ru" openInNewTab="true"/>

Сортировка

Сортировка для DB-источника данных (структура sortcols)

Sortcols – это строка с именами столбцов, идущими через запятую, по которым нужно выполнить сортировку строк в гриде. Вначале строки стоит Order by, если в гриде нужно сортировать по какому либо полю. Если сортировка не требуется, передается пустая строка.

Сортировка для источников данных Celesta и Jython (список sortColumnList)

sortColumnList -- это объект типа java.util.List<ru.curs.gwt.datagrid.model.Column> -- список столбцов, по которым выполнена сортировка в гриде. В силу ограничения JS-компонента dgrid сейчас этот список состоит только из одного столбца, поскольку dgrid поддерживает сортировку только по одному столбцу. В будущем, после того как dgrid будет поддерживать сортировку по нескольким столбцам, sortColumnList будет содержать несколько элементов. При этом, sortColumnList[index].getId() возвращает идентификатор столбца, а sortColumnList[index].getSorting() направление сортировки (ASC, DESC).

Параметры firstrecord и pagesize

В эти параметры приходят данные о номере записи (firstrecord), с которой необходимо начать отображение в гриде, и количестве записей для отображения (pagesize). Эти параметры необходимо использовать для выбора нужного количества записей. В pagesize приходит параметр (pagesize) из первой процедуры. Но если пользователь указал в браузере явно количество строк на странице, то параметр pagesize из первой процедуры игнорируется и в этот параметр приходит число, указанное пользователем.

Типовой шаблон процедуры

declare @filters as varchar(1024)
declare @whereclause as varchar(1024)
declare @orderbyclause as varchar(1024)
set @filters = 'текст, который сцепляет наименование полей со значением фильтров'
set @whereclause='where 1=1 and(' + main_context + ' and ' + add_context + ' and ' + @filters + ')'
set @orderbyclause = (select 
                        case
                            when @sortcols ='' then 'order by при необходимости,сортировка по умолчанию' 
                            else @sortcols
                        end)
#-Тип сортировки desc/asc необходимо передавать через пробел с названием столбца для сортировки.
EXEC 'Запрос' + @whereclause + @orderbyclause
set @gridsettings = 'настройка отображения'

В таблице может быть задан столбец ~~id, содержащий идентификаторы для каждой записи. В случае отсутствия такого столбца идентификатором будет считаться номер записи в таблице(!). Идентификаторы записей должны быть уникальны! Рекомендуется либо не задавать идентификаторы вообще, либо задавать их для всех записей. Явное задание идентификаторов может быть полезно при получении состояния грида через related контекст.

При этом если в dataset есть столбец с названием ~~properties, то он не отображается в таблице, а в нем может находиться информация о ссылках при четырех различных событиях, следующего вида:

<properties>
        <styleClass name="grid-record-bold"/>
        <styleClass name="grid-record-italic"/>
	<event name="row_double_click">
		<action>
    		</action>
	</event>
	<event name="row_single_click">
		<action>
		</action>
	</event>
        <event name="cell_double_click" column="name">
                <action>
		</action>
	</event>
        <event name="cell_single_click" column="name">
                <action>
		</action>
	</event>
        <event name="row_selection">
                <action>
		</action>
	</event>
</properties>

styleClass - задает дополнительный класс CSS для записи. Переданный стиль устанавливается у каждой строки (<tr>) записи. Классов можно задать несколько.

Для событий типа "row_selection" принято одно важное допущение: действия у событий для всех строк должны иметь один и тот же набор элементов. В ближайшем будущем текущий способ задания таких действий будет переделан.

Загрузка файлов с сервера из грида

Появился новый тип столбцов -- DOWNLOAD. С каждым таким столбцом связана хранимая процедура загрузки файла. Общий список процедур задается в описании элемента:

	<tab id="07" name="Скачивание файлов из грида">
		<element id="0701" type="grid" proc="grid_download_load">
			<proc id="11" name="grid_download1" type="DOWNLOAD" />
			<proc id="12" name="grid_download2" type="DOWNLOAD" />
		</element>		
	</tab>

а связь столбец-процедура устанавливается в описании столбца в процедуре для отображения грида:

ALTER PROCEDURE [dbo].[grid_download_load]
 ...
AS
BEGIN
 ...
Declare @gridsettings_str as varchar(max)
set @gridsettings_str='<gridsettings>
<labels>
<header>
<h3>Порталы</h3>
</header>
</labels>
        <columns>
        <col id="Название" width="100px" /> 
        <col id="Файл1"  width="100px" type="DOWNLOAD" linkId="11"/>         
        <col id="Логотип" width="250px" type="LINK"/>
        <col id="Файл2"  width="100px" type="DOWNLOAD" linkId="12"/>                 
        <col id="URL" width="150px" type="LINK"/>
        </columns>
<properties flip="false" pagesize="22" profile="grid.nowidth.properties" autoSelectRecordId="3" 
 autoSelectRelativeRecord="false" autoSelectColumnId="URL"/>
</gridsettings>' 
 ...
END
  • При этом, в каждой ячейке столбца с типом DOWNLOAD появляется кнопка, по которой происходит загрузка файла. Иконка на кнопку задается в файле:

webapps\%webappname%\resources\internal\fileDownload.PNG.

  • В качестве содержимого ячейки можно использовать HTML.
  • Если текстовые данные в ячейке отсутствуют, то считается, что файла для загрузки нет и кнопка не показывается. Если все же необходимо отобразить кнопку в случае отсутствия данных в ячейке, то необходимо задать ее содержимое как "enableDownload".

Типовой шаблон процедуры для загрузки файла:

ALTER PROCEDURE [dbo].[grid_download1]
	@main_context varchar(512),
	@add_context varchar(512),
	@filterinfo xml,
	@session_context xml,
	@element_Id varchar(512),
	@record_Id varchar(512),
	@filename varchar(64) output,
	@file varbinary(MAX) output,
	@error_mes varchar(512) output	
AS
BEGIN
	SET NOCOUNT ON;
	
	DECLARE @navigator xml
	EXEC [generationtree] '', @navigator OUTPUT
	SET @file = CAST(@navigator AS varbinary(MAX))
	SET @filename='navigator_'+@record_Id+'.xml'	
	
	SET @error_mes=''
	RETURN 0
END


Загрузка файлов с сервера из грида GET-методом

По умолчанию, загрузка файла из грида происходит при помощи POST-метода сервлета загрузки. Иногда, необходимо загружать файл при помощи GET-метода. Для этого в содержимом ячейки должен присутствовать параметр

;downloadFileByGetMethod=true

Пример

def getData(context, main, add, filterinfo, session, elementId, sortColumnList, firstrecord, pagesize):
    
    data = u'''
    <records>
        <rec>
            <name>Тест1</name>
            <file>Загрузка файла методом GET;downloadFileByGetMethod=true</file>
            <_x007e__x007e_id>1</_x007e__x007e_id>            
        </rec>
        <rec>
            <name>Тест2</name>
            <file>Загрузка файла методом POST_2;downloadFileByGetMethod=false</file>
            <_x007e__x007e_id>2</_x007e__x007e_id>
        </rec>
        <rec>
            <name>Тест2</name>
            <file>Загрузка файла методом POST_3</file>
            <_x007e__x007e_id>3</_x007e__x007e_id>
        </rec>
    </records>'''

    res = JythonDTO(data, None)
    return res

При этом, будет вызвана та же самая функция решения, что и при загрузке файла методом POST, с тем отличием, что в ней будут отсутствовать значения main, add и filter - контекстов.

Пример

def downloadFile(context, main, add, filterinfo, session, elementId, recordId):
    result = FileInputStream(u"E:\Downloads\_Test\src.pdf")    

    return JythonDownloadResult(result, "test.pdf")


Описание профайлов настроек грида

Профайл настроек грида - это файл в %userdata%/gridproperties, содержащий настройки для конкретного грида. Профайл можно задать в gridsettings в хранимой процедуре грида. Если профайл не задан - используется профайл дефолтный профайл "default.properties", используемый по умолчанию. Поэтому в каталоге %userdata%/gridproperties всегда должен находиться этот профайл. В профайле задаются настройки оформления и поведения грида, слабо зависящие от конкретных данных. Настройки, явно зависящие от данных - например, ширины столбцов, размер страницы грида, задаются напрямую в хранимой процедуре. Ниже приведен список настроек грида с примерами возможных значений (там где назначение настройки не очевидно, добавлены комментарии):

Параметр в файле настроек грида Параметр в <gridsettings>\<properties> Возможные значения Комментарий LiveGrid, TreeGrid, PageGrid
def.columnheader.hor.align columnheaderHorAlign LEFT CENTER RIGHT Выравнивание заголовка столбцов
def.column.hor.align Переопределяется в настройках столбцов LEFT CENTER RIGHT
def.num.column.hor.align Переопределяется в настройках столбцов LEFT CENTER RIGHT
def.str.column.hor.align Переопределяется в настройках столбцов LEFT CENTER RIGHT
def.date.column.hor.align Переопределяется в настройках столбцов LEFT CENTER RIGHT
def.image.column.hor.align Переопределяется в настройках столбцов LEFT CENTER RIGHT
def.link.column.hor.align Переопределяется в настройках столбцов LEFT CENTER RIGHT
def.date.values.format dateValuesFormat SHORT MEDIUM LONG FULL
def.column.value.display.mode columnValueDisplayMode SINGLELINE MULTILINE AUTOFIT Переносить/не переносить текст на другую строчку SINGLELINE - перенос слов не происходит
def.column.width Переопределяется в настройках столбцов 100px или 10%
def.value.font.color valueFontColor #000000 Если задать данные цвета, то они конвертируются в inline CSS стили и перекрывают внешние стили.
def.value.bg.color valueBgColor #000000 Имейте это в виду!
def.value.font.size valueFontSize 1.1em Нужно задать числовое значение и единицу измерения слитно. Разрешается использовать любые допустимые единицы CSS: em (высота шрифта элемента), ex (высота символа х), пункты (pt), пикселы (px), проценты (%) и др. За 100% берется размер шрифта родительского элемента. Если единица не задана - используется em
def.value.font.bold valueFontBold true false
def.value.font.italic valueFontItalic true false
def.value.font.underline valueFontUnderline true false
def.value.font.strikethrough valueFontStrikethrough true false
def.visible.pages.count visiblePagesCount 5 максимальное отображаемое число ссылок на страницы грида; если страниц будет меньше, чем def.visible.pages.count, то будет отображаться их реальное число
def.pages.block.duplicate.limit pagesBlockDuplicateLimit 5 число записей в гриде, при достижении которого блок с навигацией по страницам будет дублироваться снизу грида Имеет смысл только для PageGrid'а
def.select.whole.record selectWholeRecord true false
single.click.before.double singleClickBeforeDouble true false
def.visible.pager visiblePager true false Показ футера грида (там, где "Отображение 1 - 16 из 81")
def.visible.toolbar visibleToolbar true false Управление видимостью тулбара Действует только для JS-гридов
def.visible.exporttoexcel.currentpage visibleExporttoexcelCurrentpage true false
def.visible.exporttoexcel.all visibleExporttoexcelAll true false
def.visible.columns.header visibleColumnsHeader true false
def.visible.copytoclipboard visibleCopytoclipboard true false
def.visible.filter visibleFilter true false
def.visible.save visibleSave true false
def.visible.field.save visibleFieldSave true false Поле "Сохранить" в гриде
def.visible.revert visibleRevert true false
def.num.column.decimal.separator numColumnDecimalSeparator Десятичный разделитель
def.num.column.grouping.separator numColumnGroupingSeparator Разделитель групп разрядов. Пробел -- 123 456 -- def.num.column.grouping.separator = " ", отсутствие разделителя -- 123456 -- def.num.column.grouping.separator =
def.select.allow.text.selection selectAllowTextSelection true false Возможность выделения текста в ячейке Действует только для JS-гридов
def.toolbar.classname toolbarClassName Название класса стиля тулбара Действует только для JS-тулбаров
def.toolbar.style toolbarStyle Стиль тулбара Действует только для JS-тулбаров
def.toolbar.createimmediately toolbarCreateImmediately true false Немедленное создание тулбара при создании грида Действует только для JS-гридов


  • Все настройки являются необязательными. Если их не задать - будут использованы значения, "зашитые" в исходном коде программы.
  • При этом, все настройки можно переопределить в функции построения метаданных грида (в <gridsettings>\<properties> или в свойствах столбцов)
<gridsettings>
    ...

	<columns>
		<col  id="Код" width="170px" type="INT"  />                
		<col  id="Название" width="400px" horAlign="RIGHT"/>        
		<col  readonly = "false" id="Картинка" width="70px" type="IMAGE" editor="{ editor: CheckBox}"/>
	</columns>

	...

	<properties   
		autoSelectRecordId="12" 
		gridHeight="450"  
		forceLoadSettings="false" 
		pagesize="50"  
		gridWidth="850px" 
		...
		visibleToolbar = "true"
		visibleSave = "true"
		...
        />

</gridsettings>

Изменение настроек на ходу

Настройки для данных - например размер шрифта, заданный в профайле, или события, связанные с записями и ячейками - применяются при любом обновлении грида. А вот настройки столбцов - выравнивание и режим отображения (def.column.value.display.mode) из профайла или точность для числовых значений из БД - применяются только после перезагрузки элемента (и соответственно не применяются при переходах между страницами, изменении сортировки и т.п.).

Перезагрузить элемент проще всего заново открыв вкладку из навигатора.

Выдача всплывающего сообщения при экспорте в Excel

При нажатии кнопки "Экспорт в Excel" (текущей страницы и грида в целом) выдается всплывающее сообщение "Начат экспорт в Excel. Это может занять несколько минут. Кликните сюда, чтобы скрыть сообщение". Сообщение закрывается автоматически через 3 секунды или по клику мышкой на него.

Внешний вид сообщения задается стилями в internalShowcase.css

  • message-popup-panel -- сама панель
  • message-popup-label -- сообщение

Создание грида типа LiveGrid

Для создания грида типа LiveGrid в описании элемента информационной панели необходимо задать атрибут subtype="EXT_LIVE_GRID", как показано ниже:

	<tab id="2" name="LiveGrid. Профайлы">
		<element id="0101" type="grid" subtype="EXT_LIVE_GRID" proc="extlivegrid_default_profile"/>
		<element id="0102" type="grid" subtype="EXT_LIVE_GRID" proc="extlivegrid_special_profile"/>
	</tab>

Изменения в тэге <properties> структуры gridsettings показаны в следующем примере:

         <properties pagesize="50" gridWidth="500px" gridHeight="700" rowHeight="80"/>
  • pagesize - количество строк в буфере LiveGrid'а (для повышения производительности рекомендуется задавать не меньше 50)
  • gridWidth - ширина LiveGrid'а, может быть задана в пикселях или процентах (значение по умолчанию 95%)
  • gridHeight - высота LiveGrid'а (значение по умолчанию 400)
  • rowHeight - высота строки (необходимо задавать правильно для корректной работы LiveGrid'а; значение по умолчанию 20)

Правильный подсчет количества записей, отображаемых гридом в режимах AUTOFIT или MULTILINE:

При задании в файле настроек grid.properties свойства

def.column.value.display.mode = AUTOFIT или MULTILINE

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

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

<styleClass name="grid-2-line-record"/>
Так как внутри каждой grid-ячейки <td> лежат
, __необходимо__ задать высоту жестко для этих
, прописав в solutiion.css свойство:
.grid-2-line-record div
{
        height:2em;
}

Height удобно указывать в em. Если хотим, чтобы в строке грида было по одной строчке текста - 1em, 2 строчки - 2em...

После этого точное значение rowHeight в gridsettings вычисляется слудующим образом: высота div + 7px. 7px - это padding, заданный по умолчанию для строк грида. top:4px and bottom:3px. Высота одной строки текста определяется свойством def.value.font.size в файле .properties этого грида. В данном примере def.value.font.size = 15px. Итого в этом примере имеем: 2*15+7=37

<gridsettings>
 ...
 <properties ... rowHeight="37" />
</gridsettings>

Особенности профайлов настроек LiveGrid'а приведены выше в разделе "Описание профайлов настроек грида"

Создание грида типа TreeGrid

Для создания грида типа TreeGrid в описании элемента информационной панели необходимо задать атрибут subtype="EXT_TREE_GRID":

	<tab id="2" name="TreeGrid. Профайлы">
		<element id="0101" type="grid" subtype="EXT_TREE_GRID" proc="exttreegrid_default_profile"/>
		<element id="0102" type="grid" subtype="EXT_TREE_GRID" proc="exttreegrid_special_profile"/>
	</tab>

Изменения в тэге <properties> структуры gridsettings показаны в следующем примере:

         <properties gridWidth = "80%" gridHeight="700" />

где

  • gridWidth - ширина TreeGrid'а. Может быть задана в пикселях или процентах (значение по умолчанию 95%).
  • gridHeight - высота TreeGrid'а (значение по умолчанию 400).

Для грида типа TreeGrid настройки pagesize и rowHeight не имеют смысла.

Особенности профайлов настроек TreeGrid'а приведены в разделе «Описание профайлов настроек грида».

Для задания картинок на ноды TreeGrid'а необходимо поместить иконки в userdatas и прописать пути в профайле настроек:

def.treegrid.icon.nodeclose = imagesingrid/TreeGridNodeClose.gif
def.treegrid.icon.nodeopen = imagesingrid/TreeGridNodeOpen.gif
def.treegrid.icon.jointclose = imagesingrid/TreeGridJointClose.gif
def.treegrid.icon.jointopen = imagesingrid/TreeGridJointOpen.gif
def.treegrid.icon.leaf = imagesingrid/TreeGridLeaf.png

Пути отсчитываются от "userdatas/general/resources/"

Шаблон хранимой процедуры следующий:

CREATE PROC 
    @main_context varchar(MAX)='',
    @add_context varchar(MAX)='',
    @filterinfo xml='',
    @session_context xml ='',    
    @element_id varchar(MAX) ='',
    @sortcols varchar(MAX) ='',  
    @parent_id varchar(512) ='',                  
    @settings xml='' output,
    @error_mes varchar(MAX)='' output

где @parent_id - идентификатор родительской записи, для которой возвращается набор child записей (если необходимо вернуть "root"-записи, надо задать @parent_id=null)

В возвращаемом наборе записей нужно задать дополнительный параметр - HasChildren. Это признак того, что данная запись имеет "потомков" (необходимо для корректной работы TreeGrid'а)

  • 1 - "потомки" есть
  • 0 - "потомков" нет

Пример процедуры для TreeGrid:

ALTER PROCEDURE [dbo].[exttreegrid_geo]
    @main_context varchar(512) ='',
    @add_context varchar(512) ='',
    @filterinfo xml='',
    @session_context xml ='',    
    @element_id varchar(512) ='',    
    @sortcols varchar(1024) ='',	
    @parent_id varchar(512) ='',        
    @gridsettings xml output,
    @error_mes varchar(512) output
AS
BEGIN
SET NOCOUNT ON;

declare @Sql varchar(8000);

IF (@parent_id IS NULL) OR (LTrim(@parent_id) = '')
BEGIN
 set @Sql = 'select Name as "Название", Id as "Код", ''imagesingrid/test.jpg'' AS [Картинка], 
1 as HasChildren, geo6_Id as "~~id", cast( ''<properties>
                    <event name="row_single_click">
                        <action>
                            <main_context>current</main_context>
                            <datapanel type="current" tab="current">
                                <element id="105">
									<add_context>''+[Name]+''</add_context>                                                                                             
                                </element> 
                            </datapanel>
                        </action>
                    </event>    
                    <event name="row_selection">
                        <action>
                            <main_context>current</main_context>
                            <datapanel type="current" tab="current">
                                <element id="105">
									<add_context>''+[Name]+''</add_context>                                                                                             
                                </element> 
                            </datapanel>
                        </action>
                    </event>                                       
            </properties>'' as xml)  as [~~properties] from geo6 where Id IS NOT NULL'
END

IF (select COUNT(*) from geo6 where geo6_Id = @parent_id) > 0
BEGIN
 set @Sql = 'select Name as "Название", Id as "Код", ''imagesingrid/test.jpg'' AS [Картинка],
 1 as HasChildren, geo5_Id as "~~id", cast( ''<properties>
                    <event name="row_single_click">
                        <action>
                            <main_context>current</main_context>
                            <datapanel type="current" tab="current">
                                <element id="105">
									<add_context>''+[Name]+''</add_context>                                                                                             
                                </element> 
                            </datapanel>
                        </action>
                    </event>    
                    <event name="row_selection">
                        <action>
                            <main_context>current</main_context>
                            <datapanel type="current" tab="current">
                                <element id="105">
									<add_context>''+[Name]+''</add_context>                                                                                             
                                </element> 
                            </datapanel>
                        </action>
                    </event>                                       
            </properties>'' as xml)  as [~~properties] from geo5 where (FJField_9 = '''+@parent_id+''') AND (Id IS NOT NULL)' 
END

IF (select COUNT(*) from geo5 where geo5_Id = @parent_id) > 0
BEGIN
 set @Sql = 'select Name as "Название", Id as "Код", ''imagesingrid/test.jpg'' AS [Картинка],
 0 as HasChildren, geo3_Id as "~~id", cast( ''<properties>
                    <event name="row_single_click">
                        <action>
                            <main_context>current</main_context>
                            <datapanel type="current" tab="current">
                                <element id="105">
									<add_context>''+[Name]+''</add_context>                                                                                             
                                </element> 
                            </datapanel>
                        </action>
                    </event>    
                    <event name="row_selection">
                        <action>
                            <main_context>current</main_context>
                            <datapanel type="current" tab="current">
                                <element id="105">
									<add_context>''+[Name]+''</add_context>                                                                                             
                                </element> 
                            </datapanel>
                        </action>
                    </event>                                       
            </properties>'' as xml)  as [~~properties] from geo3 where (FJField_9 = '''+@parent_id+''') AND (Id IS NOT NULL)'  
END

IF (select COUNT(*) from geo3 where geo3_Id = @parent_id) > 0
BEGIN
 SET @error_mes = 'Вызов процедуры для города' 
 RETURN 1
END

IF LTRIM(@sortcols)!=''
BEGIN
 set @Sql = @Sql+' '+@sortcols
END

EXEC(@Sql)


Declare @gridsettings_str as varchar(max)
set @gridsettings_str='<gridsettings>
        <labels>
            <header><h3 class="testStyle">Хедер tree-грида</h3></header>
            <footer><h3 class="testStyle">Футер tree-грида</h3></footer>            
        </labels>
        <columns>
			<col id="Название" width="200px"/>        
			<col id="Код" width="50px"/>
			<col id="Картинка" width="50px" type="IMAGE"/>
        </columns>        
						<action>
							<main_context>current</main_context>
                            <datapanel type="current" tab="current">
                                <element id="105">
	                                <add_context>current</add_context>
                                </element>                                                                   
                            </datapanel>
                        </action>
         <properties flip="false" pagesize="50" gridHeight="500" autoSelectRecordId="9"  autoSelectRelativeRecord="false" 
totalCount="0"/></gridsettings>'         
set @gridsettings=CAST(@gridsettings_str as xml)

END

Создание грида типа PageGrid

Для создания грида типа PageGrid в описании элемента информационной панели необходимо задать атрибут subtype="EXT_PAGE_GRID", как показано ниже:

       <tab id="2" name="Начало">
               <element id="2" type="GRID"  subtype="EXT_PAGE_GRID" proc="extpagegrid_bal"/>
               <element id="5" type="webtext" proc="webtext_filter_and_add" hideOnLoad="true" />
               <element id="3" type="chart" proc="chart_bal_extgridlive" hideOnLoad="true" />
       </tab>

Примеры работы можно посмотреть в развертке дистрибутива на CI на тестовой базе в

  • Фичи \ Версия 2 \ PageGrid1
  • Фичи \ Версия 2 \ PageGrid2

Параметры структуры gridsettings, задаваемой в тэге <properties>, аналогичны параметрам для LiveGrid'а. При этом, параметр rowHeight здесь не имеет смысла и его задавать не нужно. Также, снимается ограничение на размер pagesize - здесь он может быть любым.

JS-гриды

База знаний по JS-гридам

Предполагается, что здесь будет содержаться полезная информация по использованию JS-гридов. Приглашаются все желающие к наполнению данного раздела!

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

Необходимо добавить следующий стиль

.dgrid-scroller {
    -moz-user-select: text !important;
    -webkit-user-select: text !important;
    -ms-user-select: text !important;
    -o-user-select: text !important;
    user-select: text !important;
}

Замечание. При этом, в процессе эксплуатации данного подхода выяснилось, что он не работает для Internet Explorer. Поэтому в файле настроек грида была сделана настройка def.select.allow.text.selection для тех же целей. Для возможности выделения текста в ячейке необходимо установить

def.select.allow.text.selection = true

В этом случае стиль .dgrid-scroller использовать не нужно

Общие замечания

  • Подключение JS-гридов в Showcase реализовано в рамках расширенного и доработанного механизма плагинов. Поэтому в качестве компоненты грида в принципе можно использовать любой подходящий JS-грид (dgrid, GridX итд), сделав соответствующий JS-адаптер. Сейчас в качестве основного подключен dgrid. Это opensource'ный компонент, использующий в своей работе библиотеку dojo (но не входящий в нее). Для него такие JS-адаптеры уже написаны.
  • Реализованы 3 типа грида -- live, page, tree -- так же как и для "старых" гридов
  • Внимание! Поскольку JS-гриды используют механизм плагинов, то при обновлении Showcase нужен не только новый war'ник, но и актуальные плагины гридов, находящиеся директориях
    • userdatas\general\plugins\liveDGrid
    • userdatas\general\plugins\pageDGrid
    • userdatas\general\plugins\treeDGrid
  • Задание JS-грида во многом похоже на задание "старого" грида, но имеет некоторые особенности
  • Крайне рекомендуется развернуть у себя тестовое решение для ознакомления с JS-гридами и дальнейшего изучения нового функционала, который будет в них появляться

Задание JS-грида в элементе датапанели

Представляет из себя некую смесь определения плагина и грида (больше, похожую на грид). Задается subtype и указывается плагин для разных типов гридов

  • subtype="JS_LIVE_GRID" plugin="liveDGrid"
  • subtype="JS_PAGE_GRID" plugin="pageDGrid"
  • subtype="JS_TREE_GRID" plugin="treeDGrid"
	<tab id="2" name="Начало">
		<element id="2" type="GRID"  subtype="JS_LIVE_GRID" proc="extlivegrid_bal" 
		    plugin="liveDGrid" >
			    <proc id="020101" name="gridToolBar" type="TOOLBAR"/>
		</element>
		 <!-- 		 refreshByTimer="true" refreshInterval="10"  -->
		<element id="0202" type="webtext" proc="webtext/selectedRowWriter.py" hideOnLoad="true" />		 
		<element id="5" type="webtext" proc="webtext_filter_and_add" hideOnLoad="true" />
		<element id="3" type="chart" proc="chart_bal_extgridlive" hideOnLoad="true" />
	</tab>

Задание источника данных для JS-грида

Такое же как для EXT-гридов, со следующими особенностями:

  • в liveGrid'е снимаются ограничения на pagesize и rowHeight
  • Внимание! В liveGrid'е идентификаторы записей должны быть уникальны по всем записям, а не только по текущему набору. Это вызвано особенностью реализации live-механизма в компоненте dgrid
  • Ширина грида может задаваться либо в пикселах (gridWidth="800px"), либо в процентах (gridWidth="100%"); высота грида только в пикселах (gridHeight="400"), поскольку для вертикальной раскладки страницы задание высоты в процентах смысла не имеет, а для табличной пока не реализовано.

Columnsets(фиксированные столбцы) и сложные заголовки

  • Задается через <columns> в <gridsettings> (некий расширенный способ задания колонок грида)
  • Поддержка фиксированных столбцов осуществляется через механизм columnsets. Columnset - это группа столбцов, имеющая свою полосу горизонтальной прокрутки

(в частности, не имеющая ее вовсе -- таким образом, получаем фиксированные столбцы).

        <columns>
					<columnset id="Набор столбцов 1" width="300px">
						<col id="Регион" width="250px"/> 
						<col id="Картинка" width="50px" type="IMAGE"/>
					</columnset>

					<columnset id="Набор столбцов 2" width="400px">
						<col id="3кв. 2005г." width="85px" precision="2"/> 
						<col id="4кв. 2005г." width="85px" precision="2"/> 
						<col id="1кв. 2006г." width="85px" precision="2"/> 
						<col id="2кв. 2006г." width="85px" precision="2"/> 
						<col id="3кв. 2006г." width="85px" precision="2"/> 
						<col id="4кв. 2006г." width="85px" precision="2"/> 
						<col id="1кв. 2007г." width="85px" precision="2"/> 
						<col id="2кв. 2007г." width="85px" precision="2"/> 
						<col id="3кв. 2007г." width="85px" precision="2"/> 
						<col id="4кв. 2007г." width="85px" precision="2"/> 
					</columnset>

					<columnset id="Набор столбцов 3" width="400px">
						<col id="1кв. 2008г." width="85px" precision="2"/> 
						<col id="2кв. 2008г." width="85px" precision="2"/> 
						<col id="3кв. 2008г." width="85px" precision="2"/> 
						<col id="4кв. 2008г." width="85px" precision="2"/> 
						<col id="1кв. 2009г." width="85px" precision="2"/> 
						<col id="2кв. 2009г." width="85px" precision="2"/> 
						<col id="3кв. 2009г." width="85px" precision="2"/> 
						<col id="4кв. 2009г." width="85px" precision="2"/> 
						<col id="1кв. 2010г." width="85px" precision="2"/> 
						<col id="2кв. 2010г." width="85px" precision="2"/> 
						<col id="3кв. 2010г." width="85px" precision="2"/> 
						<col id="4кв. 2010г." width="85px" precision="2"/> 
						<col id="1кв. 2011г." width="85px" precision="2"/> 
						<col id="2кв. 2011г." width="85px" precision="2"/> 
					</columnset>
        </columns>
    • Чтобы не появлялась горизонтальная полоса прокрутки (фиксированные столбцы) ширина columnset'а должна быть больше суммарной ширины столбцов, входящих в этот columnset.
  • Сложные заголовки представляют собой некую древовидную структуру заголовков над фактическими столбцами грида (столбцами с данными).
        <columns>
	        <columnheader id="Заголовок" style="text-align:center;">
						<col id="Регион" width="250px"/> 
						<col id="Картинка" width="50px" type="IMAGE"/>
		</columnheader>

	        <columnheader id="Годы 2005-2006" style="text-align:center;">
						<columnheader id=" 2005" style="text-align:center;">
							<col id="3кв. 2005г." width="85px" precision="2"/> 
							<col id="4кв. 2005г." width="85px" precision="2"/> 
                                                </columnheader>
						<columnheader id=" 2006" style="text-align:center;">
							1<col id="1кв. 2006г." width="85px" precision="2"/> 
							<col id="2кв. 2006г." width="85px" precision="2"/> 
							<col id="3кв. 2006г." width="85px" precision="2"/> 
							<col id="4кв. 2006г." width="85px" precision="2"/> 
						</columnheader>
                </columnheader>

	        <columnheader id="Годы 2007-2008" style="text-align:center;">
						<columnheader id=" 2007" style="text-align:center;">
							<col id="1кв. 2007г." width="85px" precision="2"/> 
							<col id="2кв. 2007г." width="85px" precision="2"/> 
							<col id="3кв. 2007г." width="85px" precision="2"/> 
							<col id="4кв. 2007г." width="85px" precision="2"/> 
						</columnheader>
						<columnheader id=" 2008" style="text-align:center;">
							<col id="1кв. 2008г." width="85px" precision="2"/> 
							<col id="2кв. 2008г." width="85px" precision="2"/> 
							<col id="3кв. 2008г." width="85px" precision="2"/> 
							<col id="4кв. 2008г." width="85px" precision="2"/> 
						</columnheader>
                </columnheader>

	        <columnheader id="Годы 2009-2011" style="text-align:center;">
						<columnheader id=" 2009" style="text-align:center;">
							<col id="1кв. 2009г." width="85px" precision="2"/> 
							<col id="2кв. 2009г." width="85px" precision="2"/> 
							<col id="3кв. 2009г." width="85px" precision="2"/> 
							<col id="4кв. 2009г." width="85px" precision="2"/> 
				                 </columnheader>
		                                 <columnheader id=" 2010" style="text-align:center;">
							<col id="1кв. 2010г." width="85px" precision="2"/> 
							<col id="2кв. 2010г." width="85px" precision="2"/> 
							<col id="3кв. 2010г." width="85px" precision="2"/> 
							<col id="4кв. 2010г." width="85px" precision="2"/> 
                                                 </columnheader>
		                                 <columnheader id=" 2011" style="text-align:center;">
							<col id="1кв. 2011г." width="85px" precision="2"/> 
							<col id="2кв. 2011г." width="85px" precision="2"/> 
                                                 </columnheader>
        	</columnheader>
        </columns>
    • Для сложного заголовка может быть задан стиль отображения

Задание иконок в treeGrid'e

В отличие от EXT-гридов, иконки задаются для каждой записи. Добавляются специальные поля в датасете

  • TreeGridNodeCloseIcon - закрытая нода,
  • TreeGridNodeOpenIcon - открытая нода,
  • TreeGridNodeLeafIcon - нода без детей,

в которых прописываются соответствующие картинки:

...'imagesingrid/TreeGridNodeClose.gif' AS [TreeGridNodeCloseIcon],...

Задание hint'а для изображений в гриде

Для значений в поле типа IMAGE в гриде можно задать хинт, который будет отображаться при наведении мышкой на соответствующую ячейку. Задается как

...'imagesingrid/test.jpg:Подсказка' AS [Картинка],...

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

Фильтры

В дополнение к "основным" кнопкам тулбара грида (Экспорт в Excel, Копирование в буфер обмена) добавлена кнопка Фильтр. Ее видимость задается в файле gridproperties, свойство def.visible.filter. По умолчанию, кнопка не видима. По нажатию появляется панель задания условий фильтра. После формирования фильтра и нажатия кнопки ОК фильтр передается в процедуру формирования грида в переменной filterinfo:

<filter>
	<gridFilterInfo>
		<filters>
			<id>1</id>
			<link>OR</link>
			<column>Регион</column>
			<condition>содержит</condition>
			<value>обл</value>
		</filters>
		<filters>
			<id>2</id>
			<link>OR</link>
			<column>Регион</column>
			<condition>содержит</condition>
			<value>моск</value>
		</filters>
		<filters>
			<id>3</id>
			<link>AND</link>
			<column>4кв. 2005г.</column>
			<condition>равно</condition>
			<value>700</value>
		</filters>
		<filters>
			<id>4</id>
			<link>AND</link>
			<column>Поле даты</column>
			<condition>равно</condition>
			<value>27.02.2014 16:46</value>
			<dateValue>2014-02-27T16:46:59.827+04:00</dateValue>
		</filters>
                <filters>
                        <id>5</id>
                        <link>OR</link>
                        <column>Регион</column>
                        <condition>список значений</condition>
                        <listOfValues>Волгоградская обл.</listOfValues>
                        <listOfValues>Калужская обл.</listOfValues>
                        <listOfValues>Брянская обл.</listOfValues>
                </filters>
	</gridFilterInfo>
</filter>

где

  • <id> - порядковый номер условия,
  • <link> - логическая связь данного условия с остальными (может быть OR или AND),
  • <column> - столбец, к которому относится условие,
  • <condition> - условие
    • для текстовых полей могут быть условия "содержит", "список значений", "равно", "начинается с", "заканчивается на", "не содержит", "не равно", "не начинается с", "не оканчивается на", "пусто",
    • для числовых полей и полей типа "Дата" могут быть условия "равно", "список значений", "больше чем", "меньше чем", "больше или равно", "меньше или равно", "не равно", "пусто",
  • <value> - значение,
  • <dateValue> - значение для поля типа "Дата",
  • <listOfValues> - выбранные значения, в случае условия фильтра "список значений".

Далее, на основе условий фильтра необходимо сформировать sql-часть WHERE с учетом особенностей данной БД (MSSQL, PostgreSQL, Celesta, ...) и добавить его в запрос формирования грида. Внимание! Автоматически формирование WHERE не происходит! (Именно поэтому кнопка Фильтр по умолчанию не видна)

Условие фильтра "список значений" (мультиселектор)

В случае условия фильтра "список значений" значения выбираются при помощи мультиселектора. Мультиселектор задается в процедуре формирования грида в <gridsettings>:

<gridsettings>
        <labels>
            <header><h3 class="testStyle">'+@main_context+' зерна, тыс. тонн </h3></header>
            <footer><h3 class="testStyle">Футер. '+@main_context+' зерна, тыс. тонн </h3></footer>            
        </labels>

		    <filters>  
			<multiselector 
				windowCaption     = "Выберите значения2"
				dataWidth         = "600px"
				dataHeight        = "450px"
				selectedDataWidth = "500px"
				visibleRecordCount = "25" 
		  		procCount         = "[dbo].[regionscount]"  
				procList          = "[dbo].[regionslist]"
         			procListAndCount  = ""
				currentValue      = ""
				manualSearch       = "false"               
				startWith          = "true"		        
                                hideStartsWith     = "false"		        
				needInitSelection  = "true"
			/>	
		    </filters>

        <columns>
         <col id="Регион" width="250px"/> 
         <col id="Картинка" width="50px" type="IMAGE"/>
...

по аналогии с тем, как это делается в xforms.

  • Если мультиселектор не задан, то условие "список значений" не появляется в списке условий фильтра.
  • В селекторные функции (procCount, procList, procListAndCount) в переменной filterinfo передаются все условия фильтра, кроме текущего (для которого вызывается мультиселектор):
<filter>
	<gridListOfValuesInfo>
		<filters>
			<id>1</id>
			<link>OR</link>
			<column>Регион</column>
			<condition>содержит</condition>
			<value>обл</value>
		</filters>
		<filters>
			<id>2</id>
			<link>OR</link>
			<column>Регион</column>
			<condition>содержит</condition>
			<value>моск</value>
		</filters>
		<filters>
			<id>3</id>
			<link>AND</link>
			<column>4кв. 2005г.</column>
			<condition>равно</condition>
			<value>700</value>
		</filters>
		<filters>
			<id>4</id>
			<link>AND</link>
			<column>Поле даты</column>
			<condition>равно</condition>
			<value>27.02.2014 16:46</value>
			<dateValue>2014-02-27T16:46:59.827+04:00</dateValue>
		</filters>
                <currentColumn>Регион</currentColumn>
	</gridListOfValuesInfo>
</filter>

Редактирование

Общие замечания

В тулбаре грида добавлены кнопки редактирования

  • Добавить запись
  • Сохранить изменения
  • Отменить изменения

Процедуры для добавления записи и сохранения изменений задаются в элементе датапанели:

	<tab id="10" name="Геодерево">
 		<element id="102" type="GRID"  subtype="JS_TREE_GRID" proc="exttreegrid_geo" plugin="treeDGrid">

		    <proc id="11" name="jstreegrid_addrecord1" type="ADDRECORD" />		    
		    <proc id="12" name="jstreegrid_save1" type="SAVE" />		    

		</element>
	</tab>

Наличие кнопок редактирования связана с наличием в датапанели соответствующей процедуры: если задана процедура добавления записи, то появляется кнопка "Добавить запись", если задана процедура сохранения изменений -- появляются кнопки "Сохранить изменения" и "Отменить изменения". При этом, если задана процедура сохранения изменений, то можно скрыть кнопки "Сохранить изменения" и "Отменить изменения", задав параметры def.visible.save = false, def.visible.revert = false в файле профайлов настроек грида.

Реализован тип сохранения "строка" -- изменения постятся только после заполнения всей строки (ухода на другую строку или при нажатии кнопки "Сохранить изменения"). При этом, одновременное редактирование нескольких записей запрещено.

Отмена изменений действует только на редактируемую запись (раньше по этой кнопке происходило обновление всего грида с запросом с сервера текущего набора данных).

При переходе строки в режим редактирования ей назначается стиль jsgrid-record-editing, который может быть использован, например, для подсветки редактируемой записи

.jsgrid-record-editing {
    background-color: yellow !important;
}

Этот стиль может задаваться в любых css файлах, которые использует Showcase, например, в файле solutionGrid.css юзердаты решения. При этом, после успешного сохранения записи или отката изменений данный стиль с записи убирается.

Настройка редактирования столбца

Настройка столбца редактирования происходит в структуре <gridsettings> в <columns> процедуры формирования метаданных грида

<columns>
   <col id="Название" width="200px" editor="{editOn: has(''touch'') ? ''click'' : ''dblclick'', editor: ''text''}" readonly = "false" />        
   <col id="Код" width="50px" editor="{editOn: has(''touch'') ? ''click'' : ''dblclick'', editor: NumberSpinner, editorArgs: {smallDelta: 0.1} }"/>
   <col id="Картинка" width="50px" type="IMAGE" readonly = "true"/>
</columns>

где

  • readonly - признак того, что столбец нередактируемый.Значение по умолчанию - false,
  • editor - настройки редактирования:
    • editOn - указывает на то, как будет происходить вход в режим редактирования для данного столбца. Значение по умолчанию
  has(''touch'') ? ''click'' : ''dblclick''

для мобильных устройств по одинарному клику, для обычных -- по двойному клику. Если editOn не задан, то редактор отображается всегда (такой режим может быть полезен, например, для редактора типа checkbox),

    • editor - задает редактор для данного столбца. Может быть либо стандартным type'ом HTML input'а, либо Dijit-видгетом. Значение по умолчанию определяется на основе типа столбца и представляет собой обычный type HTML input'а,
    • editorArgs - параметры инпута или видгета.
Отключение скролла мышки в виджете NumberSpinner

Для отключения скролла мышки нужно в editorArgs задать параметр disableMouseWheel: true

<columns>
   ...
   <col id="Код" width="50px" editor="{editOn: has(''touch'') ? ''click'' : ''dblclick'', editor: NumberSpinner, editorArgs: {smallDelta: 0.1, disableMouseWheel: true} }"/>
   ...
</columns>

Добавление записи

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

Пример процедуры для MSSQL

ALTER PROCEDURE [dbo].[jstreegrid_addrecord1]
	@main_context varchar(512),
	@add_context varchar(512),
	@filterinfo xml,
	@session_context xml,
	@element_Id varchar(512),

	@addrecorddata xml,

        @gridaddrecordresult xml output,

	@error_mes varchar(2048) output	
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

INSERT INTO Debug (context) VALUES (@addrecorddata)


DECLARE @currentRecordId varchar(2048)
SET @currentRecordId=(select @addrecorddata.value('(/addrecorddata/currentRecordId)[1]','varchar(MAX)'))


DECLARE @name varchar(2048)
DECLARE @Id INTEGER
DECLARE @ParentId uniqueidentifier

SET @name = (SELECT Name FROM geo6 where geo6_Id =  @currentRecordId)
IF @name IS NOT NULL
BEGIN
	SET @Id = (SELECT Id FROM geo6 where geo6_Id =  @currentRecordId)
	SET @name = @name+'_New'
	INSERT INTO geo6 (Name, Id) VALUES (@name, @Id)
END

SET @name = (SELECT Name FROM geo5 where geo5_Id =  @currentRecordId)
IF @name IS NOT NULL
BEGIN
	SET @Id = (SELECT Id FROM geo5 where geo5_Id =  @currentRecordId)+10000
	SET @name = @name+'_New'
	SET @ParentId = (SELECT FJField_9 FROM geo5 where geo5_Id =  @currentRecordId)
	INSERT INTO geo5 (Name, Id, FJField_9) VALUES (@name, @Id, @ParentId)
END

SET @name = (SELECT Name FROM geo3 where geo3_Id =  @currentRecordId)
IF @name IS NOT NULL
BEGIN
	SET @Id = (SELECT Id FROM geo3 where geo3_Id =  @currentRecordId)+10000
	SET @name = @name+'_New'
	SET @ParentId = (SELECT FJField_9 FROM geo3 where geo3_Id =  @currentRecordId)
	INSERT INTO geo3 (Name, Id, FJField_9) VALUES (@name, @Id, @ParentId)
END


Declare @gridaddrecordresult_str as varchar(max)
set @gridaddrecordresult_str='
<gridaddrecordresult>
</gridaddrecordresult>'

set    @gridaddrecordresult=CAST(@gridaddrecordresult_str as xml)
	

--	SET @error_mes='Ошибка при добавлении записи'
--	RETURN 30


  set @error_mes = 'Запись успешно добавлена'
  RETURN 555;


--	SET @error_mes=''
--	RETURN 0

	
END

где

  • @addrecorddata xml - входной параметр вида
  <addrecorddata>
    <currentRecordId>82D10F4B-CEF4-4E8D-B3D3-B2D8A25BBB6F</currentRecordId>
  </addrecorddata>
    • currentRecordId - идентификатор текущей записи
  • @gridaddrecordresult xml output - выходной параметр вида,
  <gridaddrecordresult></gridaddrecordresult>
    • сейчас пока не используется, сделан для возможности возвращать какие-то параметры в будущем

Пример процедуры для Jython'а

# coding: utf-8
from ru.curs.showcase.core.jython import JythonProc
from ru.curs.showcase.app.api import UserMessage
from ru.curs.showcase.app.api import MessageType
from ru.curs.showcase.core import UserMessageFactory
from ru.curs.showcase.core.jython import JythonDTO
from ru.curs.showcase.app.api.grid import GridAddRecordResult 


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



class jsTreeGrid_AddRecord1(JythonProc):
    def gridAddRecord(self, context, elId, addRecordData1):
        global main, add, session, filterContext, elementId, addRecordData 
        main = context.getMain()
        if context.getAdditional():
            add = context.getAdditional()
        session = context.getSession()
        if context.getFilter():
            filterContext = context.getFilter()
        elementId = elId
        
        addRecordData = addRecordData1
        
        return mainproc()


def mainproc():

    res = GridAddRecordResult(UserMessageFactory().build(555, u"Запись успешно добавлена из Jython"))
    return res

if __name__ == "__main__": 
    mainproc();

процедура возвращает объект класса GridAddRecordResult

Пример процедуры для Челесты

# coding: utf-8
from g1._g1_orm import testCursor
from ru.curs.showcase.app.api import UserMessage
from ru.curs.showcase.app.api import MessageType
from ru.curs.showcase.core import UserMessageFactory
from ru.curs.showcase.core.jython import JythonDTO
from ru.curs.showcase.core.jython import JythonDownloadResult
from ru.curs.gwt.datagrid.model import Column
from java.io import ByteArrayInputStream
from java.lang import String
from ru.curs.showcase.app.api.grid import GridSaveResult
from ru.curs.showcase.app.api.grid import GridAddRecordResult

...    

def gridAddRecord(context, main, add, filterinfo, session, elementId, addRecordData):
    print 'Добавление новой записи.'
    print 'User %s' % context.userId
    print 'main "%s".' % main
    print 'add "%s".' % add
    print 'filterinfo "%s".' % filterinfo
    print 'session "%s".' % session
    print 'elementId "%s".' % elementId
    print 'addRecordData: %s' % addRecordData

...    
    
    res = GridAddRecordResult(UserMessageFactory().build(555, u"Запись успешно добавлена из Челесты"))
    return res

Сохранение изменений

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

Пример процедуры для MSSQL

ALTER PROCEDURE [dbo].[jstreegrid_save1]
	@main_context varchar(512),
	@add_context varchar(512),
	@filterinfo xml,
	@session_context xml,
	@element_Id varchar(512),

	@savedata xml,

        @gridsaveresult xml output,
 
	@error_mes varchar(2048) output	
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

INSERT INTO Debug (context) VALUES (@savedata)
--INSERT INTO Debug (string) VALUES (@savedata)



Declare @gridsaveresult_str as varchar(max)
set @gridsaveresult_str='
<gridsaveresult>
	<properties refreshAfterSave="true" />
</gridsaveresult>'

set    @gridsaveresult=CAST(@gridsaveresult_str as xml)

--	SET @error_mes='Ошибка при сохранении данных'
--	RETURN 10


  set @error_mes = 'Данные успешно сохранены'
  RETURN 555;

END

где

  • @savedata xml - входной параметр вида
<savedata>
  <data>
   <readonly>false</readonly>
   <id>3E44D7EA-D797-45E9-BD28-6F85786324DB</id>
   <HasChildren>1</HasChildren>
   <editor>save</editor>
   <col1>Уральский ФО_New_ввввввв</col1>
   <col3><a><img border="0" src="solutions/default/resources/imagesingrid/test.jpg"></a></col3>
   <col2>28</col2>
  </data>

  <columns>
   <col2>Код</col2>
   <col1>Название</col1>
   <col3>Картинка</col3>
   </columns>
</savedata>
    • data - отредактированные данные,
    • columns - соответствие между условными идентификаторами столбцов(col, col2,...) и реальными названиями столбцов,
  • @gridsaveresult xml output - выходной параметр вида
<gridsaveresult>
  <properties refreshAfterSave="true" />
</gridsaveresult>
    • refreshAfterSave - задает необходимость обновления грида после отработки процедуры сохранения данных

Пример процедуры для Jython'а

# coding: utf-8
from ru.curs.showcase.core.jython import JythonProc
from ru.curs.showcase.app.api import UserMessage
from ru.curs.showcase.app.api import MessageType
from ru.curs.showcase.core import UserMessageFactory
from ru.curs.showcase.core.jython import JythonDTO
from ru.curs.showcase.app.api.grid import GridSaveResult 


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



class jsTreeGrid_Save1(JythonProc):
    def gridSaveData(self, context, elId, saveData):
        global main, add, session, filterContext, elementId, editorData 
        main = context.getMain()
        if context.getAdditional():
            add = context.getAdditional()
        session = context.getSession()
        if context.getFilter():
            filterContext = context.getFilter()
        elementId = elId
        
        editorData = saveData
        
        return mainproc()

def mainproc():
    
    res = GridSaveResult(UserMessageFactory().build(555, u"Данные успешно сохранены из Jython"))
    res.setRefreshAfterSave(1);  #будем обновлять грид 
    return res

if __name__ == "__main__": 
    mainproc();

процедура возвращает объект класса GridSaveResult

Пример процедуры для Челесты

def gridSaveData(context, main, add, filterinfo, session, elementId, editorData):
    print 'Сохранение отредактированных данных.'
    print 'User %s' % context.userId
    print 'main "%s".' % main
    print 'add "%s".' % add
    print 'filterinfo "%s".' % filterinfo
    print 'session "%s".' % session
    print 'elementId "%s".' % elementId
    print 'editorData: %s' % editorData
    
    
    res = GridSaveResult(UserMessageFactory().build(555, u"Данные успешно сохранены из Челесты"))
    res.setRefreshAfterSave(0);
    return res

Action'ы редактирования

Добавлены два вида action'ов -- на добавление записи и сохранение отредактированных данных.

Задаются в <properties> записей

<properties>
                    <event name="row_save_data">
                        <action>
                            <main_context>current</main_context>
                            <datapanel type="current" tab="current">
                                <element id="105">
  				  <add_context>''+[Name]+''_row_save_data</add_context>                                                                                             
                                </element> 
                            </datapanel>
                        </action>
                    </event>    



                    <event name="row_add_record">
                        <action>
                            <main_context>current</main_context>
                            <datapanel type="current" tab="current">
                                <element id="105">
				  <add_context>''+[Name]+''_row_add_record</add_context>                                                                                             
                                </element> 
                            </datapanel>
                        </action>
                    </event>    

...

</properties>

Поле "Сохранить" в гриде

Задается при помощи настроек

  • def.visible.field.save = true/false в файле профайла настроек грида,
  • или же visibleFieldSave = "true"/"false" в <gridsettings>\<properties>.

В гриде появляется дополнительное поле шириной 30px в конце набора полей (крайнее справа). В каждой ячейке этого поля находится кнопка с "дискеткой", по нажатию на которую происходит сохранение редактируемой записи.

Разные замечания

1. На записи может быть задан атрибут readonly -- признак того, что данная запись не может быть отредактирована. Задается в <properties> записей

<properties>

  <readonly value="true"/>

  <readonly value="true" column="Код"/>
  <readonly value="false" column="Название"/>

...

</properties>

Если нужно запретить редактирование не всей записи, а конкретной ячейки, то необходимо задать столбец в атрибуте column. Если столбец не задан, то запрещается редактирование всей записи.

2. Особенность компонента dgrid такова, что столбец может быть либо редактируемым, либо для его ячеек будет выдаваться хинт при наведении мыши. В случае, если столбец должен редактироваться, и в то же время должен выдаваться хинт, предлагается такой механизм. Предположим, что в ячейке должен выводиться текст "РБК", тогда вместо него выводить

  <div title="РБК">РБК</div>

3. Примеры редактирования для MSSQL, Jython'a и Челесты содержатся в тестовых базе и юзердатах, которые находятся на Дженкинсе, рядом с варником.

Частичное обновление

Общие замечания

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

Процедура для частичного обновления (с типом "PARTIALUPDATE") задается в элементе датапанели:

	<tab id="10" name="Геодерево">
 		<element id="102" type="GRID"  subtype="JS_TREE_GRID" proc="exttreegrid_geo" 
		    plugin="treeDGrid">
			<proc id="020101" name="gridToolBar" type="TOOLBAR"/>
 		    
			<proc id="14" name="exttreegrid_geo_partial" type="PARTIALUPDATE" /> 

		</element>
		<element id="0202" type="webtext" proc="webtext/selectedRowWriter.py" hideOnLoad="true" />		    
		<element id="105" type="webtext" proc="webtext_filter_and_add" hideOnLoad="true" />
	</tab>

Настройка action'а

Добавлен атрибут partial_update у действия и у отдельных элементов действия. Указывает на то, что нужно выполнить частичное обновление всех или отдельных элементов панели. Значение атрибута у действия перекрывается значениями атрибута элементов. Примечание: в настоящее время учитывается только для JS-гридов и плагинов, для остальных элементов игнорируется.

			<action keep_user_settings="true" partial_update="true">
				<main_context>current</main_context>
				<datapanel type="current" tab="current">

					<element id="102" keep_user_settings="true" partial_update="true">
						<add_context>ElementId='+@elementId+' Name='+@columnName+', Id='+@id+'</add_context>
					</element>

				</datapanel>
			</action>

Id записей, для которых необходимо обновление, задаются в add_context'e в произвольном формате. Также, здесь может быть задана любая дополнительная информация. add_context передается на вход процедуры частичного обновления, где из него можно получить идентификаторы записей для обновления, с учетом формата, в котором они задавались.

Процедура частичного обновления

Имеет тот же формат, что и процедура полного обновления.

ALTER PROCEDURE [dbo].[exttreegrid_geo_partial]
    @main_context varchar(512) ='',
    @add_context varchar(512) ='',
    @filterinfo xml='',
    @session_context xml ='',    
    @element_id varchar(512) ='',    
    @sortcols varchar(1024) ='',	
    @parent_id varchar(512) ='',        
    @gridsettings xml output,
    @error_mes varchar(512) output
AS
BEGIN
SET NOCOUNT ON;

select 
 'Молдавия_Частично_обновленный' as "Название"
, 42 as "Код"
, '85CD7E79-F23E-454B-B4F5-E81EC3755D99' as "~~id"
, NULL as "parent"
, 1 as HasChildren
, cast('<properties></properties>' as xml) as [~~properties]
UNION ALL
select 
 'Латвия_Частично_обновленный' as "Название"
, 41 as "Код"
, '75C8920D-4F26-485B-BD10-223BE4ABC14F' as "~~id"
, NULL as "parent"
, 1 as HasChildren
, cast('<properties></properties>' as xml) as [~~properties]
UNION ALL
select 
 'Респ. Башкортостан_Частично_обновленный' as "Название"
, 52 as "Код"
, '938C2366-38CE-4F42-8200-0AD2805982C2' as "~~id"
, 'AFAF2D58-7016-4A0B-B228-8DC765444A37' as "parent"
, 1 as HasChildren
, cast( '<properties>
                  <event name="row_single_click">
                        <action>
                            <main_context>current</main_context>
                            <datapanel type="current" tab="current">
                                <element id="105">
									<add_context>Респ. Башкортостан_Частично_обновленный2</add_context>                                                                                             
                                </element> 
                            </datapanel>
                        </action>
                    </event>    

                    <event name="row_selection">
                        <action>
                            <main_context>current</main_context>
                            <datapanel type="current" tab="current">
                                <element id="105">
									<add_context>Респ. Башкортостан_Частично_обновленный2</add_context>                                                                                             
                                </element> 
                            </datapanel>
                        </action>
                    </event>                                       


            </properties>' as xml) as [~~properties]
UNION ALL
select 
 'Пермская обл._Частично_обновленный' as "Название"
, 51 as "Код"
, '5C2DF7C9-24DC-45A3-87BB-6B9A37C95AC0' as "~~id"
, 'AFAF2D58-7016-4A0B-B228-8DC765444A37' as "parent"
, 1 as HasChildren
, cast('<properties></properties>' as xml) as [~~properties]
UNION ALL
select 
 'АЛЬМЕЖ_Частично_обновленный' as "Название"
, 71 as "Код"
, '7FDE1D27-6F73-4F22-A7B9-0A9D7F504873' as "~~id"
, '90321F70-379E-4B22-A3A5-5FF8DDAE2527' as "parent"
, 0 as HasChildren
, cast('<properties></properties>' as xml) as [~~properties]
END
  • При этом, параметры @sortcols, @parent_id, @gridsettings в настоящее время не используются, но могут быть задействованы в будущем.
  • Переменная @add_context содержит тот самый add_context, который задавался в action'e, включая Id записей, которым необходимо обновление.
  • Для тригрида в записи необходимо задать идентификатор родительской записи parent
  • Для записей можно задавать не только данные, которые будут обновлены, но и метаинформацию (action'ы на записи).

Пример процедуры для Jython'а

# coding: utf-8

from ru.curs.showcase.core.jython import JythonProc
from ru.curs.showcase.app.api import UserMessage
from ru.curs.showcase.app.api import MessageType
from ru.curs.showcase.core import UserMessageFactory
from ru.curs.showcase.core.jython import JythonDTO


# init vars
main = ""
add = ""
session = ""
filterContext = ""
elementId = ""
sortcols = None #объект типа java.util.List<ru.curs.gwt.datagrid.model.Column>


class jsTreeGridPartialUpdate (JythonProc):
    def getRawData(self, context, elId, scols):
        global main, add, session, filterContext, elementId, sortcols
        main = context.getMain()
        if context.getAdditional():
            add = context.getAdditional()
        session = context.getSession()
        if context.getFilter():
            filterContext = context.getFilter()
        elementId = elId
        sortcols = scols
        return mainproc()


def mainproc():
    data = u'''
    <records>
        <rec>
            <name>Тест1_Частичное обновление</name>
            <code>1</code>            
            <_x007e__x007e_id>1</_x007e__x007e_id>
        </rec>
        <rec>
            <name>Тест4_Частичное обновление</name>
            <code>4</code>
            <_x007e__x007e_id>4</_x007e__x007e_id>
        </rec>
    </records>'''
    
    settings = None
    
    res = JythonDTO(data, settings)
    return res
    

if __name__ == "__main__": 
    mainproc();

Пример процедуры для Челесты

def gridPartialUpdate(context, main, add, filterinfo, session, elementId, sortColumnList, parentId ):
    print 'Get grid data and setting from Celesta Python procedure.'
    print 'User %s' % context.userId
    print 'main "%s".' % main
    print 'add "%s".' % add
    print 'filterinfo "%s".' % filterinfo
    print 'session "%s".' % session
    print 'elementId "%s".' % elementId
    
    data = u'''
    <records>
        <rec>
            <name>Тест1_Частичное обновление</name>
            <code>1</code>            
            <_x007e__x007e_id>1</_x007e__x007e_id>
            <HasChildren>1</HasChildren>
        </rec>
        
        <rec>        
            <name>Тест4_Частичное обновление</name>
            <code>4</code>
            <_x007e__x007e_id>4</_x007e__x007e_id>
        </rec>
        
    </records>'''
    
    settings = None

    res = JythonDTO(data, settings)

    return res

Начальная сортировка гридов

Начиная с версии Showcase 3.5.1.3868-build806 (Mar 20, 2015 4:21:08 PM), в гридах можно задавать начальную сортировку.

Задается в <gridsettings>:

...

        <columns>
		<col id="_Id" width="100px"/>                
		<col id="Name" width="400px"/>        
        </columns>

        <sorting>
		<sort column="Name" direction="DESC"/>        
        </sorting>

...

где

  • column -- идентификатор столбца,
  • direction -- направление сортировки (ASC, DESC).

В настоящее время грид поддерживает сортировку только по одному столбцу, но после того как в dojo-шном компоненте dgrid появится сортировка по нескольким столбцам, она будет сделана и в шоукейзовском гриде. Тогда положение столбца в <sorting> будет определять порядок сортировки столбца в гриде.

При этом, если грид задается двумя процедурами и столбцы задаются явно (заданы <columns> в процедуре метаданных), то при первом вызове процедуры получения данных в нее будет передаваться начальная сортировка, заданная в <sorting>.

Если же грид задается одной процедурой или <columns> не заданы, то начальная сортировка не передается и нужно будет сортировать датасет самостоятельно, чтобы он соответствовал начальной сортировке, заданной в <sorting>.

Переход на новую версию dgrid, 0.3 -> 0.4

Начиная с версии 4.1.0.4026-build924 (Aug 12, 2015 10:57:40 PM), в trunk-ветке Showcase произошел переход на новую версию dgrid, с 0.3 на 0.4. В связи с этим,

  • Внимание! Необходимо полное тестирование всего функционала гридов (live, page, tree), а также плагина навигатор
  • При задании новых гридов в решении сохранена совместимость со старой версией
  • В новой версии НЕ поддерживаются IE 6,7, quirks mode, Presto-based Opera
  • Частичное обновление
    • поле parent в возвращаемом датасете НЕ нужно
    • сортировка не нужна
    • раньше, если обновляемая строка не находилась на уровне root в тригриде, то сбивалось ее положение (перемещалась в начало уровня). В новой версии это исправлено.
  • Теперь для редактируемого столбца также выдается хинт
  • Пропало надоедливое сообщение в консоли броузера "Error: multipleDefine"


Рефакторинг гридов

Начиная с версии Build 4.1.0.4119-build1031 (Dec 17, 2015 7:25:25 PM), в trunk-ветке Showcase произошел рефакторинг гридов. В связи с этим,

  • Внимание! Необходимо полное тестирование всего функционала гридов (live, page, tree)
  • Внимание! При задании гридов в решении в общем случае отсутствует совместимость со старой версией

1. Поддерживаются типы гридов JS_LIVE_GRID, JS_PAGE_GRID, JS_TREE_GRID. Типы гридов EXT_LIVE_GRID, EXT_PAGE_GRID, EXT_TREE_GRID, а также "советский" грид больше не поддерживаются.

2. Теперь поддерживается только задание грида двумя процедурами (включая tree-грид), задание одной процедурой не поддерживается. Параметры этих процедур остались прежними. В процедуре получения данных для tree-грида в конец списка параметров добавился параметр parent_id -- идентификатор родительской записи (кроме Jython'а -- там, как и прежде, parent_id может быть получен из контекста).

3. Столбцы грида должны быть заданы явно в ф-ции метаданных.

4. В процедуре получения метаданных в <properties> поддерживается только задание выделенной записи по идентификатору и смещению(для page-грида)

  <properties>
...
    autoSelectRecordId="85CD7E79-F23E-454B-B4F5-E81EC3755D99" autoSelectOffset="100"
...
  </properties>

Задание выделенной записи по номеру (относительному или абсолютному) больше не поддерживается.

5. Проверка на уникальность Id записей теперь должна производиться в функции построения грида решения.

6. В файле пропертей грида убраны настройки

def.pages.block.duplicate.limit
def.visible.columns.customizer
def.visible.columngroups.customizer
def.visible.records.selector

def.treegrid.icon.nodeclose
def.treegrid.icon.nodeopen
def.treegrid.icon.jointclose
def.treegrid.icon.jointopen
def.treegrid.icon.leaf

def.visible.striperows
def.column.showlines

как неиспользуемые.

Для page-грида подключена настройка

  def.visible.pages.count

-- количество номеров страниц в навигациии от текущей страницы. Раньше задавалось фиксированным и было равно 2.

Альтернативные методы обновления tree-грида -- только текущий или только нижний уровни

Указание на то, что нужно обновлять только текущий или только нижний уровни задается через action.

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

В action добавлен атрибут current_level_update. Указывает на то, что нужно выполнить обновление текущего уровня всех или отдельных элементов панели. Значение атрибута у действия перекрывается значениями атрибута элементов. Примечание: в настоящее время учитывается только для tree-гридов, для остальных элементов игнорируется.

			<action keep_user_settings="true" current_level_update="true">
				<main_context>current</main_context>
				<datapanel type="current" tab="current">

					<element id="102" keep_user_settings="true" current_level_update="true">
						<add_context>ElementId='+@elementId+' Name='+@columnName+', Id='+@id+'</add_context>
					</element>

				</datapanel>
			</action>

При задании обновления только нижнего уровня происходит обновление потомков выделенной записи. При этом, обновление других раскрытых уровней не происходит. За счет этого увеличивается скорость обновления tree-грида.

В action добавлен атрибут child_level_update. Указывает на то, что нужно выполнить обновление нижнего уровня всех или отдельных элементов панели. Значение атрибута у действия перекрывается значениями атрибута элементов. Примечание: в настоящее время учитывается только для tree-гридов, для остальных элементов игнорируется.

			<action keep_user_settings="true" child_level_update="true">
				<main_context>current</main_context>
				<datapanel type="current" tab="current">

					<element id="102" keep_user_settings="true" child_level_update="true">
						<add_context>ElementId='+@elementId+' Name='+@columnName+', Id='+@id+'</add_context>
					</element>

				</datapanel>
			</action>


Для обновления родительской записи необходимо задать функцию в датапанели с типом UPDATEPARENTS

	<tab id="10" name="Основной функционал">
 		<element id="441" type="GRID"  subtype="JS_TREE_GRID" proc="grid_new_data_geo"
		    plugin="treeDGrid">
			<proc id="442" name="grid_new_meta_geo" type="METADATA"/>
			
   			<proc id="020101" name="toolbar_grid_new" type="TOOLBAR"/>
   			
		    <proc id="11" name="jstreegrid_addrecord1" type="ADDRECORD" />
		    <proc id="12" name="jstreegrid_save1" type="SAVE" />
 		    
			<proc id="14" name="partial_grid_new_geo" type="PARTIALUPDATE" /> 
		
   			<proc id="15" name="parents_grid_new_geo" type="UPDATEPARENTS" />
		</element>
		<element id="443" type="webtext" proc="webtext/selectedRowWriter.py" hideOnLoad="true" />		    
		<element id="105" type="webtext" proc="webtext_filter_and_add" hideOnLoad="true" />
	</tab>

Пример такой функции

ALTER PROCEDURE [dbo].[parents_grid_new_geo]
    @main_context varchar(512) ='',
    @add_context varchar(512) ='',
    @filterinfo xml='',
    @session_context xml ='',
	@element_id varchar(512) ='',    
    @sortcols varchar(1024) ='',		

    @firstrecord int = 1,
    @pagesize int = 20,   

    @parent_id varchar(512) =''        
AS
BEGIN
SET NOCOUNT ON;

select 
 'Оренбургская обл._Обновленный_Parent' as "Название"
, 16 as "Код"
, '0254F639-41EC-4563-88FB-6B5D63EF247A' as "~~id"
, 'AFAF2D58-7016-4A0B-B228-8DC765444A37' as "parentId"

, cast('<properties></properties>' as xml) as [~~properties]

END

Формат функции такой же как для функций получения данных грида и частичного обновления.

  • parent_id - идентификатор записи для которой необходимо возвратить данные.

Возможность раскрытия всех записей tree-грида

Настройка задается в properties грида:

<gridsettings>

	...

<properties

	...

expandAllRecords="true"  

	...

/>

	...

</gridsettings>

Указание на то, что при первом открытии tree-грида будут раскрыты все записи (пока актуально только для tree-грида). Внимание! Данный сценарий может потребовать много времени для открытия грида. Использовать с осторожностью! (значение по умолчанию равно false).

Перенос плагинов гридов из юзердат в варник

Начиная с версии 5.2.4473-build1374 в trunk-ветке Showcase плагины гридов перенесены в варник.

  • Таким образом, при использовании гридов больше не нужно обновлять плагины.
  • Также, больше не нужно указывать плагин при задании грида в датапанели:
	<tab id="2" name="Начало">
		<element id="2" type="GRID" subtype="JS_LIVE_GRID" proc="extlivegrid_bal">
			    <proc id="020101" name="gridToolBar" type="TOOLBAR"/>
		</element>
		 <!-- 		 refreshByTimer="true" refreshInterval="10"  -->
		<element id="0202" type="webtext" proc="webtext/selectedRowWriter.py" hideOnLoad="true" />		 
		<element id="5" type="webtext" proc="webtext_filter_and_add" hideOnLoad="true" />
		<element id="3" type="chart" proc="chart_bal_extgridlive" hideOnLoad="true" />
	</tab>


Загрузка скриптов гридов при открытии окна Showcase

Начиная с версии 5.2.4488-build1382 в trunk-ветке Showcase появилась возможность загрузки скриптов гридов при открытии окна Showcase. Управляется параметром

grids.preload=true/false

в generalapp.properties. Значение по умолчанию false


Кнопки в гриде (toolbar)

Общее описание

Toolbar в гридах реализован в версии Showcase от 9.04.13 и старше. Для его реализации используется следующее описание элемента информационной панели (для случая treegrid'a):

<datapanel>
 <tab id="01" name="Dynamic ToolBar">
  <element id="0101" type="GRID" subtype="EXT_TREE_GRID" proc="exttreegrid_geo_icons">
   <proc id="010101" name="treeGridToolBar" type="TOOLBAR"/>
   <related id="0101"/>
  </element>
  <element id="selectedRowWriter" type="webtext" proc="webtext/selectedRowWriter.py" hideOnLoad="true" />
 </tab>
</datapanel>

Шаблон процедуры типа TOOLBAR следующий:

CREATE PROCEDURE [dbo].[treeGridToolBar]

@main_context nvarchar(512)='',
@add_context nvarchar(512)='',
@filterinfo xml='',
@session_context xml='',
@data xml output

AS
BEGIN

declare
@id nvarchar(50)=''
set @id = @session_context.value('(/sessioncontext/related/gridContext[@id=11]/currentRecordId)[1]', 'nvarchar(50)');
if @id is null begin
set @id = ''
end

set @data='
<gridtoolbar>
 <separator/>
 <item text="Кнопка один" img="imagesingrid/test.jpg" hint="Item one" disable="false" >
  <action>
   <main_context>current</main_context>
   <datapanel type="current" tab="current">
    <element id="selectedRowWriter">
     <add_context></add_context>
    </element>
   </datapanel>
  </action>
 </item>
 <group text="Группа кнопок" >
  <item text="Элемент группы 1" hint="Item two" disable="true" />
  <item text="Элемент группы 2" hint="Item three" disable="false" />
  <separator/>
  <group text="Подгруппа кнопок" disable="false" >
   <item text="Элемент подгруппы 1" hint="Item four" disable="true" />
   <separator/>
   <item text="Элемент подгруппы 2" hint="Item five" disable="false">
    <action>
     <main_context>current</main_context>
     <datapanel type="current" tab="current">
      <element id="selectedRowWriter">
       <add_context>Item five click. Id='+@id+'</add_context>
      </element>
     </datapanel>
    </action>
   </item>
  </group>
 </group>
</gridtoolbar>
';

END

Вид и функционал кнопок описывает переменная @data. Каждый элемент item в toolbar имеет следующие основные атрибуты:

  • text - название кнопки, отображаемое в toolbar
  • img - изображение, отображаемое рядом с названием
  • hint - подсказка/комментарий к элементу, отображающийся при наведении мышки
  • visible - атрибут, позволяющий скрывать кнопку при значении "false"
  • disable - атрибут, позволяющий делать кнопку неактивной при значении "true"

В переменную @session_context процедуры передается id текущего выбранного элемента, что позволяет делать toolbar динамическим. В частности, можно сделать кнопки активными/видимыми, в зависимости от выбранного элемента, к примеру:

declare @disable_1 as nvarchar(50);

set @disable_1 =
CASE
when @id = 0 then 'false'
when @id = '' then 'false'
else 'true'

end

...

<item text="Элемент группы 1" hint="Item two" disable="'+@disable_1+'" />

...

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

Признак готовности тулбара для нажатия кнопок

Для того, чтобы отличить состояния тулбара "тулбар формируется (происходит загрузка данных тулбара)" и "тулбар сформирован (готов к работе)" в тулбар добавляются соответствующие стили:

  • в процессе формирования тулбара добавляется стиль "awaiting-response",
  • а когда тулбар полностью загружен и готов к работе -- "ready".

Переход на JS-тулбар грида

В рамках перевода пользовательского интерфейса Showcase на JavaScript-библиотеку dojo, начиная с версии 5.2.4528-build1415 (29.12.2016 16:21:08), в trunk-ветке Showcase произошел переход на JS-тулбар грида (раньше тулбар был сделан на основе GWT библиотеки Sencha).

Пример JS-тулбара

	<gridtoolbar className="testJSToolbar" style="font-style: italic;">

	<item text="Серверное действие"  hint="Серверное действие"
            iconClassName="testJSToolbarButton"  >
		<action >
			<main_context>current</main_context>                        
			<server>
				<activity id="srv02" name="sc_add_to_debug_console_adapter">
					<add_context>
					      add
					</add_context>
				</activity>
			</server>
		</action>
	</item>

        <item id="copyToClipboard" text="Расширенный экспорт в буфер обмена"  hint="Экспорт в буфер обмена"
            iconClassName="testJSToolbarButton"  >
        </item>

	<item text="Частичное обновление"  hint="Частичное обновление"
          className="class2" iconClassName="testJSToolbarButton" popupText="Всплывающее сообщение">
		<action keep_user_settings="true" partial_update="false"       >
			<main_context>current</main_context>
			<datapanel type="current" tab="current">

				<element id="441" keep_user_settings="true" partial_update="true">
					<add_context>ElementId='+@elementId+' Name='+@columnName+', Id='+@id+'</add_context>
				</element>
			</datapanel>
		</action>
	</item>

	<item text="Полное обновление" hint="Полное обновление"  style="background-color: blue;">
		<action>
			<main_context>current</main_context>
			<datapanel type="current" tab="current">
				<element id="441" keep_user_settings="true">
					<add_context>ElementId='+@elementId+' Name='+@columnName+', Id='+@id+'</add_context>
				</element>
			</datapanel>
		</action>
	</item>

	<separator/>

	<item text="Обновление текущего уровня"  hint="Обновление текущего уровня">
		<action keep_user_settings="true"   current_level_update = "true"   >
			<main_context>current</main_context>
			<datapanel type="current" tab="current">
				<element id="441" keep_user_settings="true"  current_level_update = "true"  >
					<add_context>ElementId='+@elementId+' Name='+@columnName+', Id='+@id+'</add_context>
				</element>
			</datapanel>
		</action>
	</item>

	<item text="Обновление нижнего уровня"  hint="Обновление нижнего уровня"
          iconClassName="testJSToolbarButton">
		<action keep_user_settings="true"  child_level_update = "true">
			<main_context>current</main_context>
			<datapanel type="current" tab="current">
				<element id="441" keep_user_settings="true"   child_level_update = "true"  >
					<add_context>ElementId='+@elementId+' Name='+@columnName+', Id='+@id+'</add_context>
				</element>
			</datapanel>
		</action>
	</item>

	<separator/>
	<item text="Item1"  hint="Item one" disable="true" iconClassName="testJSToolbarButton">
	</item>
	<group text="Item2Group"   iconClassName="testJSToolbarButton" >
		<item text="Item21" hint="Item two"  disable="true"/>
		<separator/>
		<item text="Item22" hint="Item three"  iconClassName="testJSToolbarButton">
			<action show_in="MODAL_WINDOW">
				<main_context>current</main_context>
				<modalwindow caption="Item22 click." height="200" width="600"/>
				<datapanel type="current" tab="current">
					<element id="443">
						<add_context>ElementId='+@elementId+', Столбец='+@columnName+', RecordId='+@id+'</add_context>
					</element>
				</datapanel>
			</action>
		</item>
	</group>

</gridtoolbar>

Отличия от предыдущей версии следующие

1. Для всего тулбара (<gridtoolbar>) добавились необязательные атрибуты

  • className - имя стиля, который будет применен к тулбару при каждом создании тулбара,
  • style - стиль, который будет применен к тулбару при каждом создании тулбара.

В <properties> ф-ции метаданных грида добавлены атрибуты

  • toolbarClassName - имя стиля, который будет применен к тулбару после создания грида,
  • toolbarStyle - стиль, который будет применен к тулбару после создания грида.
<properties ...	 toolbarClassName="testGridToolbar"  toolbarStyle="font-style: italic;" ... />

В этих атрибутах можно задать высоту тулбара. Если ни один из атрибутов не задан, будет установлена высота тулбара по умолчанию 29px.

Аналогичные свойства добавлены и в файл пропертей грида

def.toolbar.classname=testGridToolbar  
def.toolbar.style=font-style: italic;


2. Для кнопок тулбара добавились необязательные атрибуты

  • className - имя стиля, который будет применен к кнопке,
  • style - стиль, который будет применен к кнопке,
  • iconClassName - имя стиля картинки на кнопке (при этом, атрибут img - путь к файлу картинки больше не поддерживается).

Пример задания iconClassName:

 iconClassName="testJSToolbarButton"
.testJSToolbarButton {
    background-image: url('../resources/imagesingrid/test.jpg');
    background-repeat: no-repeat;
    width: 18px;
    height: 18px;
    text-align: center;"
}

3. Для кнопок тулбара добавлен необязательный атрибут

  • popupText - всплывающее сообщение, которое появляется при нажатии на кнопку (раньше такое сообщение появлялось только для стандартных кнопок экспорта в Excel).

4. Сделана возможность кастомизации стандартных кнопок.

Для этого на кнопках тулбара добавлен необязательный атрибут

  • id - идентификатор кнопки. Именно по нему тулбар понимает, что это одна из стандартных кнопок и нужно выполнить стандартное действие, связанное с этой кнопкой.

Если кнопка нестандартная, то этот атрибут задавать не нужно.

Возможные значения id:

  • exportToExcelCurrentPage - экспорт в Excel текущей страницы,
  • exportToExcelAll - экспорт в Excel всего грида,
  • copyToClipboard - копирование в буфер обмена,
  • filter - вызов панели фильтра,
  • addRecord - добавление записи,
  • save - сохранение записи,
  • revert - откат значений редактируемой записи.

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

Для кнопок экспорта в Excel добавлен необязательный атрибут fileName - имя Excel-файла. Если он не задан, то используется имя по умолчанию - table.

Пример,

<item id="copyToClipboard" text="Расширенный экспорт в буфер обмена"  hint="Экспорт в буфер обмена"
            iconClassName="testJSToolbarButton"  >
</item>

<item id="exportToExcelCurrentPage" text="Экспорт в Excel"  hint="Экспорт в Excel с заданием имени файла"
            iconClassName="testJSToolbarButton"  fileName="excelFile">
</item>

5. Сделана возможность скачивания (DOWNLOAD) файла по кнопке тулбара.

  • Добавлен атрибут кнопки downloadLinkId, который должен содержать идентификатор процедуры скачивания файла, задаваемой в описании элемента грида в датапанели (по аналогии с механизмом скачивания файла из самого грида).
  • Параметры процедуры скачивания файла из тулбара такие же, как и в процедуре скачивания файла из грида.
  • При этом, на кнопке может быть определено действие. По нажатию такой кнопки, скачивание файла и выполнение действия будут происходить одновременно (асинхронно).

Пример датапанели

	<tab id="4" name="Скачивание файлов из грида">
		<element id="0401" type="grid" subtype="JS_LIVE_GRID" proc="grid_new_data_download_load"
			plugin="liveDGrid" >
			<proc id="40101" name="grid_new_meta_download_load" type="METADATA"/>
			
			<proc id="020101" name="toolbar_grid_new_download_file" type="TOOLBAR"/>
						
			<proc id="11" name="grid_download1" type="DOWNLOAD" />
			<proc id="12" name="grid_download2" type="DOWNLOAD" />
		</element>		
	</tab>

Пример описания кнопки

	<item text="Скачать файл 2"  hint="Скачать файл"  downloadLinkId="12" iconClassName="testJSToolbarButton" >
		<action >
			<main_context>current</main_context>                        
			<server>
				<activity id="srv02" name="sc_add_to_debug_console_adapter">
					<add_context>
					      add
					</add_context>
				</activity>
			</server>
		</action>
	</item>

В данном примере, по нажатию кнопки "Скачать файл 2" будет скачиваться файл с помощью процедуры grid_download2, а также выполняться действие.

6. Появились ограничения, связанные с особенностями библиотечной dojo-компоненты тулбара:

  • раньше, если кнопки не помещались на тулбаре, появлялся треугольничек в конце тулбара и непоместившиеся кнопки отображались в выпадающем меню. В новом тулбаре такого функционала нет.
  • для выпадающего меню с кнопками (тег <group>) поддерживается только один уровень вложенности,
  • как упоминалось ранее, не поддерживается задание имени файла картинки на кнопке (атрибут img).

С учетом данных ограничений, переход на новый JS-тулбар грида обладает частичной совместимостью со старой версией.

Режим немедленного создания тулбара при создании грида

В данном режиме при создании грида сразу же создается пустой тулбар, а затем, после отработки основной ф-ции создания тулбара, он заполняется кнопками.

  • Настройка в файле пропертей грида def.toolbar.createimmediately=true/false
  • Настройка в <gridsettings>\<properties> ф-ции метаданных грида toolbarCreateImmediately=true/false
  • По умолчанию, этот режим отключен
  • Внимание! К пустому тулбару применяются стили, заданные в <gridsettings>\<properties> ф-ции метаданных грида (toolbarClassName, toolbarStyle). Поэтому, для

корректного отображения пустого тулбара необходимо правильное задание этих стилей. В частности, высоты тулбара -- она должна совпадать с высотой заполненного кнопками тулбара

Признак готовности тулбара

Ранее, признаком готовности тулбара (при создании грида, или же при переходе на другую запись) являлось мигание кнопок тулбара, если они нажимались до того, как тулбар полностью построен. Это поведение было изменено, в соответствии с http://jira.curs.ru/browse/PK-128

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

Внимание! Для того, чтобы картинка на кнопке также становилась неактивной, нужно задать стиль неактивной картинки следующим образом:

 iconClassName="testJSToolbarButton"
.testJSToolbarButton {
    background-image: url('../resources/imagesingrid/test.jpg');
    background-repeat: no-repeat;
    width: 18px;
    height: 18px;
    text-align: center;"
}

.dijitDisabled .testJSToolbarButton {
    background-image: url('../resources/imagesingrid/testDisabled.jpg');
    background-repeat: no-repeat;
    width: 18px;
    height: 18px;
    text-align: center;"
}

Нужно указать .dijitDisabled и задать путь к файлу неактивной картинки.

Если в качестве метода получения неактивной картинки выбрано преобразование активной в черно-белый вид, то это можно сделать при помощи ресурса http://convertimage.net/online-photo-effects/black-and-white-photo-fx.asp

Lyra-гриды как элементы датапанели

Задание Lyra-грида в элементе датапанели

Задается subtype и указывается плагин (по аналогии с другими типами гридов в Showcase)

  • subtype="JS_LYRA_GRID" plugin="lyraDGrid"
  <tab id="5" name="showcase (actions, ...)">	
 	<element id="521" type="GRID" subtype="JS_LYRA_GRID" proc="testgrain.testgridform3.TestGridForm3" 
	    plugin="lyraDGrid">
	    
		<proc id="020101" name="testgrain.toolbar_grid_new" type="TOOLBAR"/>
	</element>
	
	<element id="443" type="webtext" proc="webtext/selectedRowWriter.py" hideOnLoad="true" />		 
	<element id="444" type="webtext" proc="testgrain.webtext_filter_and_add" hideOnLoad="true" />
  </tab>

Задание Lyra-грида в модуле __init__.py

В файле __init__.py должен быть прописан импорт челестовского модуля, в котором задается Lyra-грид.

#coding utf-8
from common.navigator import navigatorsParts
from testgrain.navigator import navLyra
print 'initializing'
import testgrain.testgridform3

Задание Lyra-грида в модуле _*.sql

В этом модуле задается sql-таблица, на основе которой будет построен Lyra-грид

create grain testgrain version '1.0';

...

create table street4(
/**
 {"width": 270, "caption": "Название"}
 */
	name varchar(40) NOT NULL,
	
        rnum int,	
 	code varchar(17) NOT NULL PRIMARY KEY,	
	socr varchar(10) NOT NULL,
	--index varchar(6) NOT NULL,
	gninmb varchar(4) NOT NULL,
	uno varchar(4) NOT NULL,
	ocatd varchar(11) NOT NULL
);

create index ix_street4 on street4 (name, code);

Задание Lyra-грида в модуле формы

Подробное описание приведено здесь

# coding=UTF-8
from lyra.gridForm import GridForm
from lyra.basicForm import form
from lyra.basicForm import formfield
from _testgrain_orm import street4Cursor

@form(
      profile='test.properties',
      
      #gridwidth='50%',
      gridheight='410px',
      
      header=u'''<h1 class="testStyle">Лира грид. Хедер</h1>''',
      footer=u'''<h1 class="testStyle">Лира грид. Футер</h1>''',
      
      defaultaction=u'''
        <action>
                            <main_context>current</main_context>
                            <datapanel type="current" tab="current">
                                <element id="443">
                                    <add_context>Действие по умолчанию</add_context>                                                                                             
                                </element> 
                                <element id="444">
                                    <add_context>Действие по умолчанию 2</add_context>                                                                                             
                                </element> 
                                
                            </datapanel>
        </action>
      '''
     )
class TestGridForm3(GridForm):  
    def __init__(self, context):
        super(TestGridForm3, self).__init__(context)
        self.createAllBoundFields()
        
        self.getFormProperties().setHeader(u'''<h1 class="testStyle">'''+context.getShowcaseContext().getMain()+'''</h1>''')
        
        self.getFormProperties().setFooter(u'''<h1 class="testStyle">Лира грид. Футер</h1>''');
        self.getFormProperties().setProfile('test.properties');
        self.getFormProperties().setGridwidth('90%');
        self.getFormProperties().setGridheight('410px');
        self.getFormProperties().setDefaultaction('<action></action>');
            
        
    def _getCursor(self, context):
        

        print context.getShowcaseContext().getMain();
        print context.getShowcaseContext().getAdditional();
        print context.getShowcaseContext().getFilter();
        print context.getShowcaseContext().getSession();
        print context.getShowcaseContext().getElementId();
        

        c = street4Cursor(context) 

#        c.orderBy('name DESC', 'code DESC')
#        c.orderBy('name aSC')
        c.orderBy('name')
        
        return c
    

    def getGridHeight(self):
        return 50
    
    def getSummaryRow(self):
        return u'''
            {
                "name": "NAME",
                "rnum": "RNUM",    
                "code": "CODE",        
                "socr": "SOCR",        
                "gninmb": "GNINMB",        
                "ocatd": "OCATD"        
            }'''

    def get_properties_(self):
        
        name = self.rec()._currentValues()[0]
        
        ret = u'''

<properties>

<!--
        <styleClass name="jslivegrid-record-bold"/>
        <styleClass name="jslivegrid-record-italic"/>
-->        
        

                    <event name="row_single_click">
                        <action>
                            <main_context>current</main_context>
                            <datapanel type="current" tab="current">
                                <element id="443">
                                    <add_context>'''+name+'''</add_context>                                                                                             
                                </element> 
                                <element id="444">
                                    <add_context>'''+name+'''</add_context>                                                                                             
                                </element> 
                                
                            </datapanel>
                        </action>
                    </event>    

  
                    <event name="row_selection">
                        <action>
                            <main_context>current</main_context>
                            <datapanel type="current" tab="current">
                                <element id="444">
                                            <add_context>'''+name+'''</add_context>   
                                </element> 
                                
                            </datapanel>
                        </action>
                    </event>     


            </properties>         
'''                  
        
        return ret

где

  • profile - название файла пропертей грида,
  • gridwidth - ширина грида,
  • gridheight - высота грида,
  • header - хедер,
  • footer - футер,
  • defaultaction - действие по умолчанию,

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

@form(...)

либо динамически в конструкторе формы

class TestGridForm3(GridForm):  
    def __init__(self, context):
        super(TestGridForm3, self).__init__(context)
        self.createAllBoundFields()
        
        self.getFormProperties().setHeader(u'''<h1 class="testStyle">'''+context.getShowcaseContext().getMain()+'''</h1>''')
        self.getFormProperties().setFooter(u'''<h1 class="testStyle">Лира грид. Футер</h1>''');
        self.getFormProperties().setProfile('test.properties');
        self.getFormProperties().setGridwidth('90%');
        self.getFormProperties().setGridheight('410px');
        self.getFormProperties().setDefaultaction('<action></action>');
  • def _getCursor(self, context): - ф-ция, в которой должен задаваться курсор,
  • def getGridHeight(self): - ф-ция, в которой задается высота грида в записях. Внимание! Для корректной работы необходимо, чтобы высота грида в пикселях была несколько больше высоты в записях. Например, если высота грида в записях 20, то высота в пикселях должна быть такой, чтобы в гриде отображалось 18 записей,
  • def get_properties_(self): - ф-ция, в которой задаются проперти на строчке грида. Она вызывается для каждой строки в момент формирования грида на сервере. id записи задавать не нужно, так как он формируется автоматически

Контексты и elementId Showcase доступны как

        context.getShowcaseContext().getMain();
        context.getShowcaseContext().getAdditional();
        context.getShowcaseContext().getFilter();
        context.getShowcaseContext().getSession();
        context.getShowcaseContext().getElementId();

Итоговая строка

Задается в модуле формы в функции getSummaryRow в виде json'а

Пример

    def getSummaryRow(self):
        return u'''
            {
                "name": "NAME",
                "rnum": "RNUM",    
                "code": "CODE",        
                "socr": "SOCR",        
                "gninmb": "GNINMB",        
                "ocatd": "OCATD"        
            }'''
  • В качестве ключей выступают идентификаторы столбцов, а в качестве значений -- значения, которые будут отображаться в итоговой строчке грида.
  • Если функция getSummaryRow не задана или возвращает null, то итоговая строка не отображается.


Lyra-гриды как компоненты Vue (lyra-grid)

Задание lyra-grid на странице

Задается как

  <lyra-grid 
  	formclass  = "testgrain.testgridform3vue.TestGridForm3vue"
	instanceid="grid1"
  	
	maincontext = "This is mainContext for lyra-vue grid1"
	addcontext  = '{"part1": "part1","part2": "part2","refreshParams": {"selectKey": "","sort": "name,code","filter": ""}}'	

	v-on:select="select1"
	v-on:dblclick="dblclicked1"	
  >	
  </lyra-grid>

где

  • formclass - питоновский класс формы лира-грида,
  • instanceid - идентификатор инстанса лира-грида (компоненты lyra-grid могут иметь одинаковый класс формы, но тогда у них должны быть разные инстансы),
  • maincontext - main context шоукейза, который будет передаваться в питоновский класс формы лира-грида,
  • addcontext - additional context шоукейза, который будет передаваться в питоновский класс формы лира-грида,
  • v-on:select - функция отклика, которая будет вызваться при выделении записи в лира-гриде,
  • v-on:dblclick - функция отклика, которая будет вызваться при двойном клике на запись в лира-гриде.

Пример задания lyra-grid на странице jsForm

<script type="text/javascript">


require(
	[ "vue/vue", "vue/bootstrap-vue"], function(Vue, bootstrapVue){
		
		Vue.use(bootstrapVue);
		
	       var example2 = new Vue({
	        	  el: '#example21',
	        	  
	        	  data: {
	        		  message: '',
        			  addcontext:
        				'{'+
	    				'"part1": "part1",'+
  	    				'"part2": "part2",'+	

  	    				'"refreshParams": {'+
  	    			    //74000004000079300
  	    			    //current
  	    				'"selectKey": "current",'+          	    				
  	    				'"sort": "name,code",'+          	    				
  	    				//'"sort": "ocatd",'+
  	    				'"filter": "filter conditions"'+
  	    				'}'+
  	    				'}'
	        		  
	          	  },
	        	  
	        	  methods: {
          	    	refresh1: function () {
          	    		lyraGridEvents.$emit(
          	    				'refresh', 
          	    				'testgrain.testgridform3vue.TestGridForm3vue',
          	    				'grid1',
          	    				'{'+
          	    				'	"part1": "part1",'+
          	    				'	"part2": "part2",'+	

          	    				'	"refreshParams": {'+
              	    			//74000004000079300          	    				
          	    				'		"selectKey": "",'+          	    				
          	    				//'		"sort": "name,code",'+          	    				
          	    				'		"sort": "ocatd",'+
          	    				'		"filter": ""'+
          	    				'	}'+
          	    				'}'
          	    				
   	    				);
	          	    },
          	    	refresh2: function () {
          	    		lyraGridEvents.$emit(
          	    				'refresh', 
          	    				'testgrain.testgridform3vue.TestGridForm3vue',          	    				
          	    				'grid2',
          	    				'{'+
          	    				'	"part1": "part1",'+
          	    				'	"part2": "part2",'+	

          	    				'	"refreshParams": {'+
          	    			//74000004000079300
          	    				'		"selectKey": "",'+
          	    				//'		"sort": "name,code",'+          	    				
          	    				'		"sort": "ocatd",'+
          	    				'		"filter": "filter conditions"'+
          	    				'	}'+
          	    				'}'
          	    				
   	    				);
	          	    },
          	    	refresh3: function () {
          	    		lyraGridEvents.$emit(
          	    				'refresh', 
          	    				'testgrain.testgridform3vue.TestGridForm3vue',          	    				
          	    				'grid3',
          	    				'{'+
          	    				'	"part1": "part1",'+
          	    				'	"part2": "part2",'+	

          	    				'	"refreshParams": {'+
          	    			//74000004000079300
          	    				'		"selectKey": "",'+
          	    				'		"sort": "name,code",'+          	    				
          	    				//'		"sort": "ocatd",'+
          	    				'		"filter": "filter conditions"'+
          	    				'	}'+
          	    				'}'
          	    				
   	    				);
	          	    },
	          	    
	      	    	refresh44: function () {
	      	    		lyraGridEvents.$emit('refresh',	'testgrain.testgridform3vue.TestGridForm3vue', 'grid1', this.addcontext);
	          	    },
	          	    
		          	clear: function () {
	              		this.message = '';
	              	},
	          	    
	          	  	select1: function (formClass, instanceId, obj) {
	                    console.log("select1");
	              		this.message = this.message+'\n'+"event:select1"+'\n'+"formClass="+formClass+", instanceId="+instanceId+'\n'+JSON.stringify(obj);
	              	},
	              	select2: function (formClass, instanceId, obj) {
	              		console.log("select2");
	              		this.message = this.message+'\n'+"event:select2"+'\n'+"formClass="+formClass+", instanceId="+instanceId+'\n'+JSON.stringify(obj);
	              	},
	              	select3: function (formClass, instanceId, obj) {
	              		console.log("select3");
	              		this.message = this.message+'\n'+"event:select3"+'\n'+"formClass="+formClass+", instanceId="+instanceId+'\n'+JSON.stringify(obj);
	              	},
	              	
	          	  	dblclicked1: function (formClass, instanceId, obj) {
	              		this.message = this.message+'\n'+"event:dblclicked1"+'\n'+"formClass="+formClass+", instanceId="+instanceId+'\n'+JSON.stringify(obj);
	              	},
	              	dblclicked2: function (formClass, instanceId, obj) {
	              		this.message = this.message+'\n'+"event:dblclicked2"+'\n'+"formClass="+formClass+", instanceId="+instanceId+'\n'+JSON.stringify(obj);
	              	},
	              	dblclicked3: function (formClass, instanceId, obj) {
	              		this.message = this.message+'\n'+"event:dblclicked3"+'\n'+"formClass="+formClass+", instanceId="+instanceId+'\n'+JSON.stringify(obj);
	              	},
	              	
	          	    
	          	  },
	          	  
	        });

	});
	
</script>

<!-- 
<link rel="stylesheet" href="js/vue/bootstrap.min.css" />
 -->
<link rel="stylesheet" href="js/vue/bootstrap-vue.css" />



<div id="example21">
  
<b-form-group
            label = "Events" label-for="textarea">
            
            <b-form-textarea style="width:1150px;"
              id="textarea" v-model="message" :rows="20" :max-rows="20">
            </b-form-textarea>
            
            <p></p>
            
            <b-form-textarea style="width:1150px;"
              id="addcontext" v-model="addcontext" :rows="3" :max-rows="3">
            </b-form-textarea>
            
  <b-button-toolbar>
    <b-button-group>  
		<b-button v-on:click="clear">Clear events</b-button>  
    </b-button-group>
    <p></p>
    <b-button-group>  
		<b-button v-on:click="refresh1">Refresh grid1</b-button>  
		<b-button v-on:click="refresh2">Refresh grid2</b-button>
 		<b-button v-on:click="refresh3">Refresh grid3</b-button>
 		<b-button v-on:click="refresh44">ALL Refresh grid1</b-button> 		
    </b-button-group>
    <p></p>
  </b-button-toolbar>            
            
</b-form-group>

  <p></p>

  <lyra-grid 
  	formclass  = "testgrain.testgridform3vue.TestGridForm3vue"
	instanceid="grid1"
  	
	maincontext = "This is mainContext for lyra-vue grid1"
	addcontext  = '{"part1": "part1","part2": "part2","refreshParams": {"selectKey": "","sort": "ocatd","filter": ""}}'	

	v-on:select="select1"
	v-on:dblclick="dblclicked1"	
	
  >	
  </lyra-grid>
  
  <p></p>
  
  <lyra-grid
  	formclass  = "testgrain.testgridform3vue.TestGridForm3vue"
   	instanceid="grid2"
  	
	maincontext = "This is mainContext for lyra-vue grid2"
	addcontext  = '{"part1": "part1","part2": "part2","refreshParams": {"selectKey": "","sort": "code","filter": "filter conditions"}}'
	
	v-on:select="select2"
	v-on:dblclick="dblclicked2"
		
  >	
  </lyra-grid>
  
  <p></p>  
  
  <lyra-grid 
  	formclass  = "testgrain.testgridform3vue.TestGridForm3vue"
  	instanceid="grid3"  	 
  	
	maincontext = "This is mainContext for lyra-vue grid3"
	addcontext  = '{"part1": "part1","part2": "part2","refreshParams": {"selectKey": "","sort": "name,code","filter": "filter conditions"}}'	
	
	v-on:select="select3"
	v-on:dblclick="dblclicked3"
		
  >	
  </lyra-grid>
  
  
</div>


Пример соответствующего класса формы лира-грида

# coding=UTF-8

import json

from lyra.gridForm import GridForm
from lyra.basicForm import form
from lyra.basicForm import formfield
from _testgrain_orm import street4Cursor

@form(
      profile='test.properties',
      
      #gridwidth='50%',
      
      gridheight='410px',
      #gridheight='300px',
      
     )


class TestGridForm3vue(GridForm):  
    def __init__(self, context):
        super(TestGridForm3vue, self).__init__(context)
        self.createAllBoundFields()
        
    def _getCursor(self, context):
        
        print 'LLLLLLLLLLLLLLL44._getCursor'
        print 'Main'
        print context.getShowcaseContext().getMain();
        print 'Additional'
        print context.getShowcaseContext().getAdditional();
        print 'Filter'
        print context.getShowcaseContext().getFilter();
        print 'Session'
        print context.getShowcaseContext().getSession();
        print 'ElementId'
        print context.getShowcaseContext().getElementId();
        print 'OrderBy'
        print context.getShowcaseContext().getOrderBy();
        

        c = street4Cursor(context)
        
        
        if context.getShowcaseContext().getAdditional() == None:
                print 'addcontext=None';
                c.orderBy('ocatd')
        else: 
                addcontext = None 
                try:
                    addcontext = json.loads(context.getShowcaseContext().getAdditional())
                    
                    print 'addcontext_json'
                    print addcontext
                    
                    try:
                    
                        print addcontext["refreshParams"]
                    
                        selectKey = addcontext["refreshParams"]["selectKey"]                    
                        sort = addcontext["refreshParams"]["sort"]
                        filter = addcontext["refreshParams"]["filter"]
                    
                        print 'selectKey="'+selectKey+'"';
                        print 'sort="'+sort+'"';
                        print 'filter="'+filter+'"';

                        if sort != "":
                        
                            arrSort = sort.split(',')
                    
                            print 'arrSort';
                            print arrSort
                    
                            c.orderBy(*arrSort)
                        else:                        
                            c.orderBy('ocatd')
                    except:       
                        c.orderBy('ocatd')                                                 
                    
                    
                except (TypeError, ValueError):
                    print 'addcontext is NOT json'
                    c.orderBy('ocatd')


        self.getFormProperties().setHeader(u'''<h2 class="testStyle">'''+context.getShowcaseContext().getMain()+'''</h2>''')
        self.getFormProperties().setFooter(u'''<h2 class="testStyle">'''+context.getShowcaseContext().getAdditional()+'''</h2>''')        

        return c


    def getGridHeight(self):
        return 50
    
    
    def get_properties_(self):
        
        
        name = self.rec()._currentValues()[0]
        
        ret = u'''

<properties>
        <styleClass name="jslivegrid-record-bold"/>
        <styleClass name="jslivegrid-record-italic"/>
            </properties>         
'''                  
        
        return ret

Описание контекстов

maincontext

  • Задается на клиентской части. Может иметь произвольный формат
  • Задается один раз при создании грида

addcontext

  • Основной контекст для передачи параметров в форму грида
  • Задается на клиентской части
  • Задается при создании грида, а также при передаче сообщений гриду
  • Имеет json формат
{
	"part1": "part1",
	"part2": "part2",	

        ...
	
	"refreshParams": {
		"selectKey": "current",		
		"sort": "name,code",
		"filter": ""
	}
}
    • part1, part2 -- произвольная часть. Может иметь любое название (не только part1, part2) и содержимое.
    • refreshParams -- параметры, использующиеся лира-гридом при обновлении
      • selectKey -- private key записи, которую необходимо выделить после обновления. Может быть конкретное значение или current - тогда после обновления грид позиционируется на текущую запись до обновления. Если selectKey не задан, то грид спозиционируется на первую запись.
      • sort -- поля сортировки
      • filter -- условия фильтра

sessioncontext

  • Формируется на сервере автоматически перед передачей в форму грида.

Сообщения, передаваемые в lyra-grid

Для передачи сообщений в lyra-grid используется шина событий lyraGridEvents.

refresh

  • Обновление грида
  • Основное сообщение для лира-грида
  • Имеет формат

lyraGridEvents.$emit('refresh', formclass, instanceid, addcontext);

    • 'refresh' - тип события,
    • formclass - класс формы лира-грида,
    • instanceid - идентификатор инстанса лира-грида
    • addcontext - addcontext с параметрами обновления грида

Пример

          	    		lyraGridEvents.$emit(
          	    				'refresh', 
          	    				'testgrain.testgridform3vue.TestGridForm3vue',
          	    				'grid1',
          	    				'{'+
          	    				'	"part1": "part1",'+
          	    				'	"part2": "part2",'+	

          	    				'	"refreshParams": {'+
          	    				'		"selectKey": "74000004000079300",'+          	    				
          	    				'		"sort": "ocatd",'+
          	    				'		"filter": ""'+
          	    				'	}'+
          	    				'}'
   	    				);

export-to-clipboard

  • Передача выделенных записей в буфер обмена
  • Имеет формат

lyraGridEvents.$emit('export-to-clipboard', formclass, instanceid);

    • 'export-to-clipboard' - тип события,
    • formclass - класс формы лира-грида,
    • instanceid - идентификатор инстанса лира-грида

Пример

lyraGridEvents.$emit('export-to-clipboard','testgrain.testgridform32vue.TestGridForm32vue','grid4');

export-to-excel

  • Экспорт грида в Excel
  • Имеет формат

lyraGridEvents.$emit('export-to-excel', formclass, instanceid, exporttype, filename);

    • 'export-to-excel' - тип события,
    • formclass - класс формы лира-грида,
    • instanceid - идентификатор инстанса лира-грида,
    • exporttype - тип экспорта ('current' - текущее содержимое, 'all' - весь грид),
    • filename - название excel файла (без расширения)

Примеры

lyraGridEvents.$emit('export-to-excel','testgrain.testgridform32vue.TestGridForm32vue','grid4', 'current', 'lyraVueExportToExcelCurrent');
lyraGridEvents.$emit('export-to-excel','testgrain.testgridform32vue.TestGridForm32vue','grid4', 'all', 'lyraVueExportToExcelAll');

file-download

  • Загрузка файла из грида
  • Имеет формат

lyraGridEvents.$emit('file-download', formclass, instanceid, procName);

    • 'file-download' - тип события,
    • formclass - класс формы лира-грида,
    • instanceid - идентификатор инстанса лира-грида,
    • procName - функция решения загрузки файла

Пример

lyraGridEvents.$emit('file-download','testgrain.testgridform5vue.TestGridForm5vue','grid5', 'testgrain.grid_download1');
  • Загрузка файла с использованием события file-download является аналогом загрузки файла по кнопке тулбара для не-vue грида.
  • При этом, доступна загрузка файлов непосредственно из записей самого грида (тип столбца DOWNLOAD).


set-columns-visibility

  • Устанавивает видимость столбцов грида
  • Имеет формат

lyraGridEvents.$emit('set-columns-visibility', formclass, instanceid, procName, columns);

    • 'set-columns-visibility' - тип события,
    • formclass - класс формы лира-грида,
    • instanceid - идентификатор инстанса лира-грида,
    • procName - функция решения загрузки файла
    • columns - параметры видимости. Это массив, задающий столбцы, видимость которых должна быть установлена. Элементы массива - объекты вида

{

id:"...", - идентификатор столбца
visible:... - видим ли столбец (true/false)

}

Примеры

var arr = [{id:"code",visible:false},{id:"socr",visible:false}];
lyraGridEvents.$emit('set-columns-visibility','testgrain.testgridform38vue.TestGridForm38vue','grid9', arr);
var arr = [{id:"code",visible:true},{id:"socr",visible:true}];
lyraGridEvents.$emit('set-columns-visibility','testgrain.testgridform38vue.TestGridForm38vue','grid9', arr);
  • Начальная видимость столбца может быть установлена на сервере посредством свойства visible

Пример

create table street4(
/**
 {"width": 270, "caption": "Название"}
 */
	name varchar(40) NOT NULL,
	
    rnum int NOT NULL,
    
	code varchar(17) NOT NULL PRIMARY KEY,	
	
	socr varchar(10) NOT NULL,
	gninmb varchar(4) NOT NULL,
/**
 {"visible": false}
 */
	uno varchar(4) NOT NULL,
	ocatd varchar(11) NOT NULL
	
	
);
  • Кроме того, видимость столбцов может задаваться интерактивно по кнопке "+-" в правой верхней части грида
  • Так же интерактивно может задаваться порядок столбцов (перетаскивание мышкой)

Сообщения, передаваемые из lyra-grid'а

select

Генерируется при выделении записи

Параметры сообщения

    • formclass - класс формы лира-грида,
    • instanceid - идентификатор инстанса лира-грида,
    • obj - объект с информацией о текущей и выделенных записях. Содержимое
    • currentRowId - идентификатор текущей записи,
    • currentRowData - данные текущей записи,
    • selection - выделенные записи

Пример

  <lyra-grid 
  	formclass  = "testgrain.testgridform3vue.TestGridForm3vue"
	instanceid="grid1"
  	
	maincontext = "This is mainContext for lyra-vue grid1"
	addcontext  = '{"part1": "part1","part2": "part2","refreshParams": {"selectKey": "","sort": "ocatd","filter": ""}}'	

	v-on:select="select1"
  >	
  </lyra-grid>
select1: function (formClass, instanceId, obj) {
 console.log("select1");
 this.message = this.message+'\n'+"event:select1"+'\n'+"formClass="+formClass+", instanceId="+instanceId+'\n'+JSON.stringify(obj);
}

dblclick

Генерируется при двойном клике на запись

Параметры сообщения

    • formclass - класс формы лира-грида,
    • instanceid - идентификатор инстанса лира-грида,
    • obj - объект с информацией о текущей и выделенных записях. Содержимое
    • currentRowId - идентификатор текущей записи,
    • currentRowData - данные текущей записи,
    • selection - выделенные записи

Пример

  <lyra-grid 
  	formclass  = "testgrain.testgridform3vue.TestGridForm3vue"
	instanceid="grid1"
  	
	maincontext = "This is mainContext for lyra-vue grid1"
	addcontext  = '{"part1": "part1","part2": "part2","refreshParams": {"selectKey": "","sort": "ocatd","filter": ""}}'	

	v-on:dblclick="dblclicked1"	
  >	
  </lyra-grid>
dblclicked1: function (formClass, instanceId, obj) {
 this.message = this.message+'\n'+"event:dblclicked1"+'\n'+"formClass="+formClass+", instanceId="+instanceId+'\n'+JSON.stringify(obj);
}


columns-info

Генерируется при создании грида. Содержит информацию о столбцах

Параметры сообщения

    • formclass - класс формы лира-грида,
    • instanceid - идентификатор инстанса лира-грида,
    • columns - массив объектов с информацией о столбцах грида

Пример

  <lyra-grid 
  	formclass  = "testgrain.testgridform38vue.TestGridForm38vue"
  	instanceid="grid9"
	maincontext = "This is mainContext for lyra-vue grid9"
	addcontext  = '{"part1": "part1","part2": "part2","refreshParams": {"selectKey": "","sort": "name,code","filter": ""}}'
	
	v-on:columns-info="columnsInfo"
  >	
  </lyra-grid>
columnsInfo: function (formClass, instanceId, obj) {
 this.message = this.message+'\n'+"event:columns-info"+'\n'+"formClass="+formClass+", instanceId="+instanceId+'\n'+JSON.stringify(obj);
}
event:columns-info
formClass=testgrain.testgridform38vue.TestGridForm38vue, instanceId=grid9
[{"id":"name","field":"name","hidden":false,"label":"Название","valueType":"STRING","sortingAvailable":"true"},{"id":"rnum","field":"rnum","hidden":false,"label":"rnum","valueType":"INT","sortingAvailable":"true"},{"id":"code","field":"code","hidden":false,"label":"code","valueType":"STRING"},{"id":"socr","field":"socr","hidden":false,"label":"socr","valueType":"STRING"},{"id":"gninmb","field":"gninmb","hidden":false,"label":"gninmb","valueType":"STRING","sortingAvailable":"true"},{"id":"uno","field":"uno","hidden":true,"label":"uno","valueType":"STRING"},{"id":"ocatd","field":"ocatd","hidden":false,"label":"ocatd","valueType":"STRING","sortingAvailable":"true"}]