Перейти к содержанию

Документ#

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

Принцип взаимодействия

Создадим документ прайс-лист, с возможностью просматривать его, редактировать, удалять и создавать новые прайс-листы. За основу возьмем пример из предыдущего занятия.

Создайте два файла в каталоге 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”)

1
2
3
4
5
{
        "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 файл:

(код “Добавление события кнопки «Сохранить»”)

1
2
3
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”)

1
2
3
4
5
{
        "caption": "Прайс лист",
        "key": "PriceListRegistry",
        "form": "./forms/PriceListRegistry"
      }

На этот раз заголовок прописываем, чтобы в меню появилась ссылка на открытие реестра.

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

Создадим новый документ, нажмите “Создать”. Откроется форма нового документа, все поля будут пустые, и код документа (последняя цифра в командной строке) будет уже не 1, а 2. Заполните дату, сохраните документ и закройте форму. В реестре появится новая строка. При нажатии на нее откроется документ.

Его можно увидеть в БД:

(код “Получение второй записи PriceList из БД”)

select * from PriceList where vcode = 2

а также можно увидеть все существующие документы:

(код “Получение всех записей PriceList из БД”)

select * from PriceList

Выделите созданный ранее документ (галочка в 1 колонке) и нажмите Удалить. В результате документ будет удален (из БД).

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

Также в реестре можно сделать фильтр по дате документа. Для этого в макете реестра добавьте на тулбар кнопку выбора периода:

(код “Добавление описания кнопки «Период»”)

<Tool caption='Период' id='toolBarItemSelectPeriod' image='current-theme/images/header/watch.png' clicked='toolBarItemSelectPeriod_clicked' />

В классе реестра добавьте реализацию нажатия на нее:

(код “Добавление события кнопки «Период»”)

1
2
3
toolBarItemSelectPeriod_clicked() {
    this.openDialog();
  }

А в модели, на которой построен реестр (PriceList.model.json) добавьте параметр:

(код “Добавление параметра dateProperty в модель”)

dateProperty: "DocumentDate",

Теперь при открытии реестра появится модальное окно выбора периода. Его всегда можно открыть нажатием на кнопку “Период” в реестре.