Using MANY datatype in UB application. #

Imagine that we want to add to our request information about the region of the city to which that request related for.
Since one request can relate to several regions of the city, we need to have the ability of multiple choice.
For this, we need the Many datatype.
Let's look at how to use it.

Creating Many datatype attribute #

First, we need to create the entity that represents the city region.
req_cityRegion.meta:

{
    "caption": "City region",
        "description": "city region",
        "descriptionAttribute": "name",
        "attributes": [
        {
            "name": "name",
            "dataType": "String",
            "size": 255,
            "caption": "Region name",
            "description": "Region name",
            "allowNull": false,
            "isMultiLang": false,
            "isUnique": true
        }
    ],
        "mixins": {
        "mStorage": {}
    }
}

Then, in the req_reqList entity add an attribute of the datatype Many that is associated with the req_cityRegion entity:

{
    "name":"regionList",
    "dataType": "Many",
    "associatedEntity": "req_cityRegion",
    "associationManyData": "req_city_region_map",
    "caption": "List city regions",
    "description": "List of city regions",
    "allowNull": true
}

After running the generateDDL, the req_cityRegion_map table will be created with two fields - sourceID(bigint) and destID(bigint).
The sourceID is an ID of the req_reqList table and the destID is an ID of the req_cityRegion table.

Now, we will add the script for filling the req_cityRegion table.
_initialData/040_fill_cityRegions.js:

module.exports = function (session) {
  const path = require('path')
  const fs = require('fs')
  const { dataLoader, csv, argv } = require('@unitybase/base')
  const conn = session.connection
  const fn = path.join(__dirname, '/req_cityRegions.csv')
  let fContent = fs.readFileSync(fn)
  if (!fContent) {
    throw new Error(`File ${fn} is empty or not exist`)
  }
  fContent = fContent.toString('utf8').trim()
  const csvData = csv.parse(fContent)
  // check existing records in the DB
  const notExisted = csvData.filter(
    (row) => !conn.lookup('req_cityRegion', 'ID',
      conn.Repository('req_cityRegion').where('name', '=', row[0]).ubql().whereList
    )
  )
  console.info('\t\tFill City Region field (req_cityRegion)')
  dataLoader.loadArrayData(conn, notExisted, 'req_cityRegion', 'name'.split(';'), [0], 1)
}

Don't forget to put these regions names to _initialData/req_cityRegions.csv:

central  
southern  
northern  
west  
eastern  

We also need a shortcut for the City Region.
We will create this with _initalData/010_create_navshortcuts.js.
Add the following code to it:

  lastID = conn.lookup('ubm_navshortcut', 'ID', {
    expression: 'code',
    condition: 'equal',
    values: { code: 'req_cityRegion' }
  })
  if (!lastID) {
    console.log('\t\tcreate `City regions` shortcut')
    lastID = conn.insert({
      fieldList: ['ID'],
      entity: 'ubm_navshortcut',
      execParams: {
        desktopID: desktopID,
        code: 'req_cityRegion',
        caption: 'City region',
        iconCls: 'fa fa-braille',
        displayOrder: 20,
        cmdCode: JSON.stringify({
          cmdType: 'showList',
          cmdData: { params: [{ entity: 'req_cityRegion', method: 'select', fieldList: '*' }] }
        }, null, '\t')
      }
    })
  } else {
    console.info('\t\tuse existed shortcut with code `req_cityRegion`', lastID)
  }

Run generateDDL and initialize commands or init script from Getting Started part of this tutorial.

And the last step - add a new field to the req_reqList-fm.def:

 { attributeName: "regionList" /*Regions name*/}

Now, we can make the multiple select for the list of city regions:
reqListForm

SELECT to the Many attribute #

Let's consider an example of a SELECT for Many datatype attribute.

We need to add a button to the toolbar to the list of applications, by clicking on which we can see the number of requests for the selected region.

For adding a button change the req_reqList entity shortcut command code to the following:

{
  "cmdType": "showList",
  "cmdData": {
    "params": [{
      "entity": "req_reqList",
      "method": "select",
      "fieldList": "*"
    }]
  },
  "customActions": [{
    "actionText": "Regions requests",
    "glyph": 62113,
    "text": "Regions requests",
    "handler": function(cmp) {
        var config = {
          cmdType: 'showForm',
          method: 'select',
          formCode: 'req_cityRegion-select',
          entity: 'req_cityRegion'
         }
        UB.core.UBApp.doCommand(config)
     
    }
  }]
}

NOTE

❗ The syntax checker for source code can produce errors. You can ignore them.


As we see, in the toolbar for the list of requests, a button has appeared:
regionListButton

By clicking on this button the req_cityRegion-select form must appear.
So, let's create this form first:
cityRegionForm


NOTE

❗ In the Model field use the name of the directory that you have created in models directory(models/_your_module_name_).


with the following code in Interface's definition section:

exports.formDef = 'request.selectRegion'
Ext.define('request.selectRegion', {
  extend: 'Ext.form.Panel',
  size: {
    width: 500,
    height: 600
  },

  items: [{
    xtype: 'ubcombobox',
    width: 400,
    name: 'cbxRegion',
    fieldLabel: UB.i18n('City region'),
    allowBlank: false,
    ubRequest: {
      entity: 'req_cityRegion',
      method: 'select',
      fieldList: ['ID', 'name']
    }
  }],
  buttons: [{
    ubID: 'btnOK',
    text: UB.i18n('ok'),
    iconCls: 'fa fa-save',
    formBind: true
  }, {
    ubID: 'btnCancel',
    text: UB.i18n('cancel'),
    iconCls: 'fa fa-times'
  }],

  initComponent: function() {
    let me = this
    me.callParent(arguments)
  }
})

Now we can press on button that we have created and see the following:
cityRegionFormView

To finish our task, we should add two buttons to our form.
The Ok button that will use regions selected in the field to find the number of requests associated with them.
And the Cancel button that just closes the form.

Add the following code to the initComponent function:

    me.down('button[ubID="btnOK"]').handler = function() {
      let region = [];
      region[0] = me.getForm().findField('cbxRegion').getValue()
      let regionName = me.getForm().findField('cbxRegion').findRecordByValue(region[0]).get('name');
      let requestInRegion = UB.Repository('req_reqList').attrs(['ID', 'regionList']).where('regionList', '=', region).selectAsObject()
        .then(function(result) {
          if (result.length) {
            $App.dialogInfo('Number of requests for the district: ' + regionName + ' - ' + result.length);
          } else {
            $App.dialogInfo('There are any requests for the district: ' + regionName);
          }
        })

    }

    me.down('button[ubID="btnCancel"]').handler = function() {
      me.up('window').close()
    }

NOTE

❗ In order to search for regions associated with requests, the query SELECT is made not to the req_cityRegion_map table in the DB, but to the original req_reqList entity that contains the regionList attribute.


The result of our work:
selectResult

Next step