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

Создадим документ прайс-лист, с возможностью просматривать его, редактировать, удалять и создавать новые прайс-листы. За основу возьмем пример из предыдущего занятия.
Создайте два файла в каталоге form:
PriceListForm.xml - файл макета
(код “Создание PriceListForm.xml”)
| <Form writeRoles='super' title='Прайс лист'>
<ToolBar>
<Tool id="toolBarItemExit" caption="Закрыть" image="current-theme/images/leftNavBar/close.png" clicked='toolBarItemExit_clicked'/>
</ToolBar>
<Layout type='bootstrap' >
<Group id='gr1' editorWidth='12' showBorder='true' >
<DateTimeInput caption='Дата' bind='DocumentDate' id='DateTimeInput1' format='dd.MM.yyyy' bindingMode='date' width='100%' captionWidth='1' editorWidth='2' />
</Group>
<Group id='gr2' editorWidth='12' >
<Grid caption='Товары' id='Grid1' bind='Detail' editorWidth='6' selectionMode='multiplecellsadvanced' height='400px' groupable='true' showAggregates='true' showStatusBar='true' cellClicked='Grid1_cellClicked' cellPainter='Grid1_cellPainter' cellValueChanged='Grid1_cellValueChanged' >
<ColumnGroup name="group1" text='Товар'>
<Column text='Группа' datafield='MaterialGroup' width='25%' >
<TextBox />
</Column>
<Column text='Наименование' datafield='Material' width='50%' >
<Lookup dataSource='../models/Material.model.json' showValueMember='false' />
</Column>
</ColumnGroup>
<Column text='Цена' datafield='Price' width='15%' cellsformat='D2' >
<NumberInput />
<ColumnAggregate type="avg" />
</Column>
<Column text="" datafield="Info" editable = 'false' width='40px' hint='Вывести информацию по товару' >
<GlyphTextBox imageRenderer='info_imageRenderer'/>
</Column>
</Grid>
</Group>
</Layout>
</Form>
|
При детальном рассмотрении видно, что у некоторых элементов управления появилось свойство bind. Это привязка элемента управления к свойству модели, при открытии документа значение этого свойства заполнится в элемент управления. Grid можно привязать только к детейлу (табличной части документа, см. detail в модели документа).
Создайте файл PriceListForm.js
(код “Создание PriceListForm.js”)
| import { Document, Colors } from 'ecosoft-lexema8'; // импорт компонентов из библиотеки ecosoft-lexema8
import template from './PriceListForm.xml'; // импорт макета документа
import model from '../models/PriceList.model.json'
export default class PriceListForm extends Document {
constructor(...args) {
super(...args);
}
// макет документа
static get template() {
return template;
}
static get binding(){
return model;
}
}
|
Как видим, теперь наш класс наследуется не от Form, а от Document. По сути это та же форма, но с дополнительным функционалом, работающим с данными из модели. За привязку документа к модели отвечает свойство binding.
Добавьте в units.json еще один пункт.
(код “Добавление документа в units.json”)
| {
"caption": "",
"key": "PriceListForm",
"form": "./forms/PriceListForm"
}
|
Свойство caption специально оставим пустым. В этом случае документ не будет видно в меню, но он будет зарегистрирован в Лексеме под именем PriceListForm. После сохранения пройдите по ссылке http://localhost:3337/#/view/PriceListForm/1.

Как вы уже, наверно, догадались произошла автоматическая загрузка данных по модели с VCode = 1 и поля документа заполнились значениями из этой модели.
Добавим возможность сохранить в БД данных из документа, в макете добавьте новый пункт в тулбар:
(код “Добавление описания кнопки «Сохранить»”)
| Tool caption="Сохранить" id="toolBarItemSave" image="current-theme/images/leftNavBar/save.png" clicked="toolBarItemSave_clicked" />
|
И его реализацию в js файл:
(код “Добавление события кнопки «Сохранить»”)
| toolBarItemSave_clicked() {
this.save();
}
|

Проверьте как работает сохранение. Измените дату на любую другую, после чего сохраните документ.
Результат можно будет увидеть в БД:
(код “Получение первой записи PriceList из БД”)
| select * from PriceList where vcode = 1
|
А также обновив страницу.
Дополним наш класс ещё функциями из предыдущего примера, чтобы привести документ к завершённому состоянию, реализовав заранее созданные функции.
| import { Document, Colors } from 'ecosoft-lexema8'; // импорт компонентов из библиотеки ecosoft-lexema8
import template from './PriceListForm.xml'; // импорт макета документа
import model from '../models/PriceList.model.json'
import infoImg from 'current-theme/images/dark/help.png';
export default class PriceListForm extends Document {
constructor(...args) {
super(...args);
}
// макет документа
static get template() {
return template;
}
static get binding(){
return model;
}
toolBarItemSave_clicked() {
this.save();
}
toolBarItemExit_clicked() {
this.close();
}
info_imageRenderer() {
return infoImg;
}
Grid1_cellClicked({ args }) {
if (args.datafield == 'Info') {
this.services.dialog.info(`${args.row.bounddata.Material.Name} по ${args.row.bounddata.Price} руб`);
}
}
Grid1_cellPainter({}, { column, data, row, value }) {
return column == 'Price' ? (value > 100 ? Colors.yellow : Colors.green)
: (column == 'Info') ? Colors.indigo
: null;
}
Grid1_cellValueChanged({ args }) {
if (args.datafield == 'Material') {
this.controls.Grid1.dataSource()[args.rowindex].MaterialGroup(args.newvalue.MaterialGroup.Name)
}
}
}
|
Избавимся от лишних элементов управления, а также при нажатии на Инфо просто будем выводить сообщение.
Реестр
А как создать еще один документ Прайс-лист? Просмотреть существующие? Удалить?
Для всех этих целей в Лексеме предусмотрен класс Registry. По сути это форма, которая содержит только один аналитический грид, в котором выводятся все документы. Из реестра происходит управление документами (открытие, создание, удаление документов).
Создайте в ./form файл PriceListRegistry.xml.
(код “Создание PriceListRegistry.xml”)
| <Form writeRoles="super" title='Прайс лист - Реестр'>
<ToolBar>
<Tool id="toolBarItemNew" caption="Создать" image="current-theme/images/leftNavBar/new.png" clicked='toolBarItemNewClick' />
<Tool id="toolBarItemRemove" caption="Удалить" image="current-theme/images/leftNavBar/trash.png" clicked='toolBarItemRemoveClick' />
</ToolBar>
<Layout>
<AnalyticalGrid id='grid' bind='*' viewName="PriceListForm" primaryKeyName='VCode' filterable="true" showFilterRow="true" width='100%' editorWidth='12' height="100%" sortable='true' selectionMode='checkbox' columnsResize='true'>
<Column text='Дата документа' datafield='DocumentDate' cellsformat='dd.MM.yyyy' width='10%' >
<DateTimeInput/>
</Column>
<ColumnGroup text='Создано' align='center' name='GroupCUser' >
<Column datafield='CDate' text='Дата' cellsformat='dd.MM.yyyy' width='5%'>
<DateTimeInput bindingMode='date' />
</Column>
<Column text='Автор' datafield='CUser' width='10%' >
<TextBox />
</Column>
</ColumnGroup>
<ColumnGroup text='Последнее изменение' align='center' name='GroupWUser' >
<Column datafield='WDate' text='Дата' cellsformat='dd.MM.yyyy' width='5%' >
<DateTimeInput bindingMode='date' />
</Column>
<Column text='Автор' datafield='WUser' width='10%' >
<TextBox />
</Column>
</ColumnGroup>
<Column text='Код' datafield='VCode' width='5%' >
<TextBox />
</Column>
</AnalyticalGrid>
</Layout>
</Form>
|
Создайте файл PriceListRegistry.js
(код “Создание PriceListRegistry.js”)
| import { Registry } from 'ecosoft-lexema8'; // импорт родительского класса для реестра
import template from './PriceListRegistry.xml'; // импорт макета документа
import model from '../models/PriceList.model.json';
export default class PriceListRegistry extends Registry {
constructor(...args) {
super(...args);
}
createDocument() {
return super.createDocument('PriceListForm');
}
static get template() {
return template;
}
static get binding() {
return model;
}
toolBarItemNewClick() {
this.createDocument();
}
toolBarItemRemoveClick() {
this.deleteSelectedDocuments();
}
}
|
Как видно, в качестве источника данных используется модель документа прайс-лист.
Добавьте объявление реестра в units.json
(код “Добавление реестра в units.json”)
| {
"caption": "Прайс лист",
"key": "PriceListRegistry",
"form": "./forms/PriceListRegistry"
}
|
На этот раз заголовок прописываем, чтобы в меню появилась ссылка на открытие реестра.

Реестр документов:

Создадим новый документ, нажмите “Создать”. Откроется форма нового документа, все поля будут пустые, и код документа (последняя цифра в командной строке) будет уже не 1, а 2. Заполните дату, сохраните документ и закройте форму. В реестре появится новая строка. При нажатии на нее откроется документ.
Его можно увидеть в БД:
(код “Получение второй записи PriceList из БД”)
| select * from PriceList where vcode = 2
|
а также можно увидеть все существующие документы:
(код “Получение всех записей PriceList из БД”)

Выделите созданный ранее документ (галочка в 1 колонке) и нажмите Удалить. В результате документ будет удален (из БД).
В реальных проектах для реестра чаще всего создаётся отдельная модель - облегчённая, которая содержит только примитивные свойства и только необходимые для вывода реестре. Это существенно ускоряет открытие реестра, ведь записей может быть очень много.
Также в реестре можно сделать фильтр по дате документа. Для этого в макете реестра добавьте на тулбар кнопку выбора периода:
(код “Добавление описания кнопки «Период»”)
| <Tool caption='Период' id='toolBarItemSelectPeriod' image='current-theme/images/header/watch.png' clicked='toolBarItemSelectPeriod_clicked' />
|
В классе реестра добавьте реализацию нажатия на нее:
(код “Добавление события кнопки «Период»”)
| toolBarItemSelectPeriod_clicked() {
this.openDialog();
}
|
А в модели, на которой построен реестр (PriceList.model.json) добавьте параметр:
(код “Добавление параметра dateProperty в модель”)
| dateProperty: "DocumentDate",
|
Теперь при открытии реестра появится модальное окно выбора периода. Его всегда можно открыть нажатием на кнопку “Период” в реестре.
