You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by se...@apache.org on 2015/07/16 22:47:42 UTC

[2/9] incubator-ignite git commit: IGNITE-843 WIP metadata: implemented fields metadata controls.

IGNITE-843 WIP metadata: implemented fields metadata controls.


Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/870f8bfa
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/870f8bfa
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/870f8bfa

Branch: refs/heads/ignite-1121
Commit: 870f8bfa0c562b6d77712a4dd55d4b871a388633
Parents: 400bc56
Author: AKuznetsov <ak...@gridgain.com>
Authored: Thu Jul 16 16:50:41 2015 +0700
Committer: AKuznetsov <ak...@gridgain.com>
Committed: Thu Jul 16 16:50:41 2015 +0700

----------------------------------------------------------------------
 .../nodejs/controllers/caches-controller.js     |  14 +-
 .../nodejs/controllers/metadata-controller.js   | 192 +++++++++++++++++--
 .../nodejs/controllers/models/metadata.json     |  11 +-
 .../nodejs/routes/metadata.js                   |   4 +-
 .../nodejs/views/configuration/metadata.jade    |  15 +-
 .../nodejs/views/includes/controls.jade         |  77 +++-----
 6 files changed, 223 insertions(+), 90 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/870f8bfa/modules/web-control-center/nodejs/controllers/caches-controller.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/controllers/caches-controller.js b/modules/web-control-center/nodejs/controllers/caches-controller.js
index b6cadcf..7af388f 100644
--- a/modules/web-control-center/nodejs/controllers/caches-controller.js
+++ b/modules/web-control-center/nodejs/controllers/caches-controller.js
@@ -215,7 +215,7 @@ controlCenterModule.controller('cachesController', ['$scope', '$http', '$saveAs'
 
         // Save cache with new name.
         $scope.saveItemAs = function () {
-           if (validate($scope.backupItem))
+            if (validate($scope.backupItem))
                 $saveAs.show($scope.backupItem.name).then(function (newName) {
                     var item = angular.copy($scope.backupItem);
 
@@ -262,7 +262,7 @@ controlCenterModule.controller('cachesController', ['$scope', '$http', '$saveAs'
             );
         };
 
-        $scope.checkIndexedTypes = function (keyCls, valCls) {
+        function tablePairValid(keyCls, valCls) {
             if (!keyCls) {
                 commonFunctions.showError('Key class name should be non empty!');
 
@@ -276,10 +276,10 @@ controlCenterModule.controller('cachesController', ['$scope', '$http', '$saveAs'
             }
 
             return true;
-        };
+        }
 
-        $scope.addIndexedTypes = function (keyCls, valCls) {
-            if (!$scope.checkIndexedTypes(keyCls, valCls))
+        $scope.tablePairAdd = function (fld, keyCls, valCls) {
+            if (!tablePairValid(keyCls, valCls))
                 return;
 
             var idxTypes = $scope.backupItem.indexedTypes;
@@ -292,8 +292,8 @@ controlCenterModule.controller('cachesController', ['$scope', '$http', '$saveAs'
                 $scope.backupItem.indexedTypes = [newItem];
         };
 
-        $scope.saveIndexedType = function (idx, keyCls, valCls) {
-            if (!$scope.checkIndexedTypes(keyCls, valCls))
+        $scope.tablePairSave = function (idx, fld, keyCls, valCls) {
+            if (!tablePairValid(keyCls, valCls))
                 return idx;
 
             var idxType = $scope.backupItem.indexedTypes[idx];

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/870f8bfa/modules/web-control-center/nodejs/controllers/metadata-controller.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/controllers/metadata-controller.js b/modules/web-control-center/nodejs/controllers/metadata-controller.js
index a4b54cc..c23da07 100644
--- a/modules/web-control-center/nodejs/controllers/metadata-controller.js
+++ b/modules/web-control-center/nodejs/controllers/metadata-controller.js
@@ -52,29 +52,142 @@ controlCenterModule.controller('metadataController', ['$scope', '$http', 'common
             curJavaType: '',
             tables: [
                 {schemaName: 'Schema1', use: true},
-                {schemaName: 'Schema1', use: true, tableName: 'Table1', keyClass: 'KeyClass1', valueClass: 'ValueClass1',
+                {
+                    schemaName: 'Schema1',
+                    use: true,
+                    tableName: 'Table1',
+                    keyClass: 'KeyClass1',
+                    valueClass: 'ValueClass1',
                     fields: [
-                        {use: true, key: true, ak: true, dbName: 'name1', dbType: 'dbType1', javaName: 'javaName1', javaType: 'javaType1'},
-                        {use: true, key: false, ak: false, dbName: 'name2', dbType: 'dbType2', javaName: 'javaName2', javaType: 'javaType2'},
-                        {use: false, key: false, ak: false, dbName: 'name3', dbType: 'dbType3', javaName: 'javaName3', javaType: 'javaType3'}
+                        {
+                            use: true,
+                            key: true,
+                            ak: true,
+                            dbName: 'name1',
+                            dbType: 'dbType1',
+                            javaName: 'javaName1',
+                            javaType: 'javaType1'
+                        },
+                        {
+                            use: true,
+                            key: false,
+                            ak: false,
+                            dbName: 'name2',
+                            dbType: 'dbType2',
+                            javaName: 'javaName2',
+                            javaType: 'javaType2'
+                        },
+                        {
+                            use: false,
+                            key: false,
+                            ak: false,
+                            dbName: 'name3',
+                            dbType: 'dbType3',
+                            javaName: 'javaName3',
+                            javaType: 'javaType3'
+                        }
                     ]
                 },
                 {schemaName: 'Schema2 with very long name', use: false},
-                {schemaName: 'Schema2', use: false, tableName: 'Table2', keyClass: 'KeyClass2', valueClass: 'ValueClass2',
+                {
+                    schemaName: 'Schema2',
+                    use: false,
+                    tableName: 'Table2',
+                    keyClass: 'KeyClass2',
+                    valueClass: 'ValueClass2',
                     fields: [
-                        {use: true, key: true, ak: true, dbName: 'name4', dbType: 'dbType4', javaName: 'javaName4', javaType: 'javaType4'},
-                        {use: true, key: false, ak: false, dbName: 'name5', dbType: 'dbType5', javaName: 'javaName5', javaType: 'javaType5'},
-                        {use: false, key: false, ak: false, dbName: 'name6', dbType: 'dbType6', javaName: 'javaName6', javaType: 'javaType6'}
-                    ]},
-                {schemaName: 'Schema2', use: false, tableName: 'Table3', keyClass: 'KeyClass3', valueClass: 'ValueClass3',
+                        {
+                            use: true,
+                            key: true,
+                            ak: true,
+                            dbName: 'name4',
+                            dbType: 'dbType4',
+                            javaName: 'javaName4',
+                            javaType: 'javaType4'
+                        },
+                        {
+                            use: true,
+                            key: false,
+                            ak: false,
+                            dbName: 'name5',
+                            dbType: 'dbType5',
+                            javaName: 'javaName5',
+                            javaType: 'javaType5'
+                        },
+                        {
+                            use: false,
+                            key: false,
+                            ak: false,
+                            dbName: 'name6',
+                            dbType: 'dbType6',
+                            javaName: 'javaName6',
+                            javaType: 'javaType6'
+                        }
+                    ]
+                },
+                {
+                    schemaName: 'Schema2',
+                    use: false,
+                    tableName: 'Table3',
+                    keyClass: 'KeyClass3',
+                    valueClass: 'ValueClass3',
                     fields: [
-                        {use: true, key: true, ak: true, dbName: 'name7', dbType: 'dbType7', javaName: 'javaName7', javaType: 'javaType7'},
-                        {use: true, key: false, ak: false, dbName: 'name8', dbType: 'dbType8', javaName: 'javaName8', javaType: 'javaType8'},
-                        {use: false, key: false, ak: false, dbName: 'name9', dbType: 'dbType9', javaName: 'javaName9', javaType: 'javaType9'},
-                        {use: false, key: false, ak: false, dbName: 'name10', dbType: 'dbType10', javaName: 'javaName10', javaType: 'javaType10'},
-                        {use: false, key: false, ak: false, dbName: 'name11', dbType: 'dbType11', javaName: 'javaName11', javaType: 'javaType11'},
-                        {use: false, key: false, ak: false, dbName: 'name12', dbType: 'dbType12', javaName: 'javaName12', javaType: 'javaType12'}
-                    ]}]
+                        {
+                            use: true,
+                            key: true,
+                            ak: true,
+                            dbName: 'name7',
+                            dbType: 'dbType7',
+                            javaName: 'javaName7',
+                            javaType: 'javaType7'
+                        },
+                        {
+                            use: true,
+                            key: false,
+                            ak: false,
+                            dbName: 'name8',
+                            dbType: 'dbType8',
+                            javaName: 'javaName8',
+                            javaType: 'javaType8'
+                        },
+                        {
+                            use: false,
+                            key: false,
+                            ak: false,
+                            dbName: 'name9',
+                            dbType: 'dbType9',
+                            javaName: 'javaName9',
+                            javaType: 'javaType9'
+                        },
+                        {
+                            use: false,
+                            key: false,
+                            ak: false,
+                            dbName: 'name10',
+                            dbType: 'dbType10',
+                            javaName: 'javaName10',
+                            javaType: 'javaType10'
+                        },
+                        {
+                            use: false,
+                            key: false,
+                            ak: false,
+                            dbName: 'name11',
+                            dbType: 'dbType11',
+                            javaName: 'javaName11',
+                            javaType: 'javaType11'
+                        },
+                        {
+                            use: false,
+                            key: false,
+                            ak: false,
+                            dbName: 'name12',
+                            dbType: 'dbType12',
+                            javaName: 'javaName12',
+                            javaType: 'javaType12'
+                        }
+                    ]
+                }]
         };
 
         $scope.metadata = [];
@@ -140,6 +253,8 @@ controlCenterModule.controller('metadataController', ['$scope', '$http', 'common
 
             $http.post('metadata/save', item)
                 .success(function (_id) {
+                    commonFunctions.showInfo('Metadata "' + item.name + '" saved.');
+
                     var idx = _.findIndex($scope.metadatas, function (metadata) {
                         return metadata._id == _id;
                     });
@@ -154,7 +269,6 @@ controlCenterModule.controller('metadataController', ['$scope', '$http', 'common
 
                     $scope.selectItem(item);
 
-                    commonFunctions.showInfo('metadata "' + item.name + '" saved.');
                 })
                 .error(function (errMsg) {
                     commonFunctions.showError(errMsg);
@@ -182,6 +296,48 @@ controlCenterModule.controller('metadataController', ['$scope', '$http', 'common
                 });
         };
 
+        function fieldValid(fld, cls) {
+            if (!fld) {
+                commonFunctions.showError('Field name should be non empty!');
+
+                return false;
+            }
+
+            if (!cls) {
+                commonFunctions.showError('Field class name should be non empty!');
+
+                return false;
+            }
+
+            return true;
+        }
+
+        $scope.tablePairAdd = function (mdl, fld, cls) {
+            if (!fieldValid(fld, cls))
+                return;
+
+            var fields = $scope.backupItem[mdl.model];
+
+            var newItem = {name: fld, className: cls};
+
+            if (fields)
+                fields.push(newItem);
+            else
+                $scope.backupItem[mdl.model] = [newItem];
+        };
+
+        $scope.tablePairSave = function (idx, mdl, fld, cls) {
+            if (!fieldValid(fld, cls))
+                return idx;
+
+            var field = $scope.backupItem[mdl.model][idx];
+
+            field.name = fld;
+            field.className = cls;
+
+            return -1;
+        };
+
         $scope.selectSchema = function (idx) {
             var data = $scope.data;
             var tables = data.tables;

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/870f8bfa/modules/web-control-center/nodejs/controllers/models/metadata.json
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/controllers/models/metadata.json b/modules/web-control-center/nodejs/controllers/models/metadata.json
index 753f643..63710e0 100644
--- a/modules/web-control-center/nodejs/controllers/models/metadata.json
+++ b/modules/web-control-center/nodejs/controllers/models/metadata.json
@@ -31,7 +31,6 @@
       "label": "Database schema",
       "type": "text",
       "model": "databaseSchema",
-      "required": true,
       "hide": "backupItem.kind == 'query'",
       "placeholder": "Input DB schema name",
       "tip": ["TODO."]
@@ -40,7 +39,6 @@
       "label": "Database table",
       "type": "text",
       "model": "databaseTable",
-      "required": true,
       "hide": "backupItem.kind == 'query'",
       "placeholder": "Input DB table name",
       "tip": ["TODO."]
@@ -77,21 +75,21 @@
     },
     {
       "label": "Query fields",
-      "type": "text",
+      "type": "fieldsMetadata",
       "model": "queryFields",
       "hide": "backupItem.kind != 'query'",
       "tip": ["TODO."]
     },
     {
       "label": "Ascending fields",
-      "type": "text",
+      "type": "fieldsMetadata",
       "model": "ascendingFields",
       "hide": "backupItem.kind != 'query'",
       "tip": ["TODO."]
     },
     {
       "label": "Descending fields",
-      "type": "text",
+      "type": "fieldsMetadata",
       "model": "descendingFields",
       "hide": "backupItem.kind != 'query'",
       "tip": ["TODO."]
@@ -115,8 +113,7 @@
     {
       "label": "Name",
       "type": "text",
-      "model": "name",
-      "required": true
+      "model": "name"
     },
     {
       "label": "Database type",

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/870f8bfa/modules/web-control-center/nodejs/routes/metadata.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/routes/metadata.js b/modules/web-control-center/nodejs/routes/metadata.js
index 283f85e..64b8763 100644
--- a/modules/web-control-center/nodejs/routes/metadata.js
+++ b/modules/web-control-center/nodejs/routes/metadata.js
@@ -42,11 +42,11 @@ router.post('/list', function (req, res) {
         });
 
         // Get all metadata for spaces.
-        db.CacheTypeMetadata.find({space: {$in: space_ids}}).sort('name').exec(function (err, metadata) {
+        db.CacheTypeMetadata.find({space: {$in: space_ids}}).sort('name').exec(function (err, metadatas) {
             if (err)
                 return res.status(500).send(err.message);
 
-            res.json({spaces: spaces, metadata: metadata});
+            res.json({spaces: spaces, metadatas: metadatas});
         });
     });
 });

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/870f8bfa/modules/web-control-center/nodejs/views/configuration/metadata.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/views/configuration/metadata.jade b/modules/web-control-center/nodejs/views/configuration/metadata.jade
index fd5ab03..7eec682 100644
--- a/modules/web-control-center/nodejs/views/configuration/metadata.jade
+++ b/modules/web-control-center/nodejs/views/configuration/metadata.jade
@@ -28,12 +28,13 @@ block content
     .docs-body(ng-controller='metadataController')
         .block-callout
             p(ng-bind-html='joinTip(screenTip)')
-        .links(ng-hide='metadata.length == 0')
-            table(st-table='metadata')
+        .links(ng-hide='metadatas.length == 0')
+            table(st-table='metadatas')
                 tbody
-                    tr(ng-repeat='row in metadata track by row._id')
+                    tr(ng-repeat='row in metadatas track by row._id')
                         td.col-sm-6(ng-class='{active: row._id == selectedItem._id}')
                             a(ng-click='selectItem(row)') {{$index + 1}}) {{row.name}}
+                hr
         panel-group(bs-collapse data-allow-multiple="false")
             .panel.panel-default
                 .panel-heading
@@ -42,14 +43,12 @@ block content
                 .panel-collapse(role="tabpanel" bs-collapse-target)
                     .panel-body
                         button.btn.btn-primary(ng-click='createItem()') &nbspAdd metadata
-                        label(style='margin-left: 15px; margin-right: 10px') for:
+                        label(style='margin-left: 6px; margin-right: 10px') for:
                         button.btn.btn-default(ng-model='template' data-template='/select' data-placeholder='Choose metadata type' bs-options='item.value as item.label for item in templates' bs-select)
                         i.tiplabel.fa.fa-question-circle(bs-tooltip data-title='{{joinTip(templateTip)}}' type='button')
-                        hr
                         form.form-horizontal(name='manualForm' ng-if='backupItem' novalidate)
-                            .panel-body
-                                .settings-row(ng-repeat='field in metadataManual')
-                                    +form-row
+                            .settings-row(ng-repeat='field in metadataManual')
+                                +form-row
                             button.btn.btn-primary(ng-disabled='manualForm.$invalid' ng-click='saveItem()') Save
                             button.btn.btn-primary.btn-second(ng-show='backupItem._id' ng-click='removeItem()') Remove
             .panel.panel-default

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/870f8bfa/modules/web-control-center/nodejs/views/includes/controls.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/views/includes/controls.jade b/modules/web-control-center/nodejs/views/includes/controls.jade
index 74481e8..f99d5dd 100644
--- a/modules/web-control-center/nodejs/views/includes/controls.jade
+++ b/modules/web-control-center/nodejs/views/includes/controls.jade
@@ -25,6 +25,32 @@ mixin tipLabel(lines)
 mixin exclamation(mdl, err, msg)
     i.fa.fa-exclamation-triangle.form-control-feedback(ng-show='inputForm["#{mdl}"].$error.#{err}' bs-tooltip data-title='#{msg}' type='button')
 
+mixin table-pair(header, tblMdl, keyFld, valFld, keyPlaceholder, valPlaceholder)
+    div
+        label.table-header #{header}:
+        +tipLabel('field.tip')
+    table.links-edit.col-sm-12(st-table=tblMdl ng-show='#{tblMdl}.length > 0')
+        tbody
+            tr.col-sm-12(ng-repeat='item in #{tblMdl}')
+                td.col-sm-6
+                    div(ng-show='field.editIdx != {{$index}}')
+                        a(ng-click='field.editIdx = $index; curKey = #{tblMdl}[$index].#{keyFld}; curValue = #{tblMdl}[$index].#{valFld}') {{$index + 1}}) {{item.#{keyFld}}} / {{item.#{valFld}}}
+                        i.tipField.fa.fa-remove(ng-click='field.editIdx = -1; #{tblMdl}.splice($index, 1)')
+                    div(ng-show='field.editIdx == {{$index}}')
+                        label.labelField {{$index + 1}})
+                        i.tipField.fa.fa-floppy-o(ng-click='field.editIdx = tablePairSave($index, field, curKey, curValue)')
+                        .input-tip
+                            .col-sm-12
+                                input.form-control.table-form-control(type='text' ng-model='curKey' placeholder=keyPlaceholder)
+                                label &nbsp;/&nbsp;
+                                input.form-control.table-form-control(type='text' ng-model='curValue' placeholder=valPlaceholder)
+    .col-sm-6
+        input.form-control(type='text' ng-model='newKey' ng-focus='field.editIdx = -1' placeholder=keyPlaceholder)
+        .settings-row
+            input.form-control(type='text' ng-model='newValue' ng-focus='field.editIdx = -1' placeholder=valPlaceholder)
+        button.btn.btn-primary.fieldButton(ng-disabled='!newKey || !newValue' ng-click='field.editIdx = -1; tablePairAdd(field, newKey, newValue)') Add
+
+
 mixin details-row
     - var lblDetailClasses = ['col-sm-4', 'details-label']
 
@@ -180,30 +206,8 @@ mixin form-row-custom(lblClasses, fieldClasses)
                 +tipField('field.tip')
                 .input-tip
                     input.form-control(type='text' ng-model='newValue' ng-focus='field.editIdx = -1'  placeholder='{{field.placeholder}}')
-        div(ng-switch-when='fieldsMetadata' ng-hide=fieldHide)&attributes(fieldCommon)
-            div
-                label.table-header {{field.label}}:
-                +tipLabel('field.tableTip')
-            table.links-edit.col-sm-12(st-table='#{fieldMdl}' ng-show='#{fieldMdl}.length > 0')
-                tbody
-                    tr.col-sm-12(ng-repeat='item in #{fieldMdl} track by $index')
-                        td.col-sm-6
-                            div(ng-show='field.editIdx != {{$index}}')
-                                a(ng-click='field.editIdx = $index; curValue = #{fieldMdl}[$index]') {{$index + 1}}) {{item | compact}}
-                                i.tipField.fa.fa-remove(ng-click='field.editIdx = -1; #{fieldMdl}.splice($index, 1)')
-                            div(ng-show='field.editIdx == {{$index}}')
-                                label.labelField {{$index + 1}})
-                                i.tipField.fa.fa-floppy-o(ng-click='#{fieldMdl}[$index] = curValue ? curValue : #{fieldMdl}[$index]; field.editIdx = curValue ? -1 : field.editIdx')
-                                .input-tip
-                                    input.form-control(type='text' ng-model='curValue' placeholder='{{field.placeholder}}')
-                        td.col-sm-1(ng-if='field.reordering')
-                            i.fa.fa-arrow-up(ng-show='$index > 0' ng-click='swapSimpleItems(#{fieldMdl}, $index, $index - 1); field.editIdx = -1;')
-                            i.fa.fa-arrow-down(ng-show='$index < #{fieldMdl}.length - 1' ng-click='swapSimpleItems(#{fieldMdl}, $index, $index + 1); field.editIdx = -1;')
-            .col-sm-6
-                button.btn.btn-primary.fieldButton(ng-disabled='!newValue || #{fieldMdl}.indexOf(newValue) >= 0' ng-click='field.editIdx = -1; #{fieldMdl} ? #{fieldMdl}.push(newValue) : #{fieldMdl} = [newValue];') Add
-                +tipField('field.tip')
-                .input-tip
-                    input.form-control(type='text' ng-model='newValue' ng-focus='field.editIdx = -1'  placeholder='{{field.placeholder}}')
+        div(ng-switch-when='fieldsMetadata' ng-hide=fieldHide)
+            +table-pair('{{field.label}}', fieldMdl, 'name', 'className', 'Field name', 'Field class full name')
         div(ng-switch-when='groupsMetadata' ng-hide=fieldHide)&attributes(fieldCommon)
             div
                 label.table-header {{field.label}}:
@@ -229,27 +233,4 @@ mixin form-row-custom(lblClasses, fieldClasses)
                 .input-tip
                     input.form-control(type='text' ng-model='newValue' ng-focus='field.editIdx = -1'  placeholder='{{field.placeholder}}')
         div(ng-switch-when='indexedTypes')
-            - var tblMdl = 'backupItem.indexedTypes'
-            div
-                label.table-header Index key-value type pairs:
-                +tipLabel('field.tip')
-            table.links-edit.col-sm-12(st-table=tblMdl ng-show='#{tblMdl}.length > 0')
-                tbody
-                    tr.col-sm-12(ng-repeat='item in #{tblMdl}')
-                        td.col-sm-6
-                            div(ng-show='field.editIdx != {{$index}}')
-                                a(ng-click='field.editIdx = $index; curKeyClass = #{tblMdl}[$index].keyClass; curValueClass = #{tblMdl}[$index].valueClass') {{$index + 1}}) {{item.keyClass}} / {{item.valueClass}}
-                                i.tipField.fa.fa-remove(ng-click='field.editIdx = -1; #{tblMdl}.splice($index, 1)')
-                            div(ng-show='field.editIdx == {{$index}}')
-                                label.labelField {{$index + 1}})
-                                i.tipField.fa.fa-floppy-o(ng-click='field.editIdx = saveIndexedType($index, curKeyClass, curValueClass)')
-                                .input-tip
-                                    .col-sm-12
-                                        input.form-control.table-form-control(type='text' ng-model='curKeyClass' placeholder='Key class full name')
-                                        label &nbsp;/&nbsp;
-                                        input.form-control.table-form-control(type='text' ng-model='curValueClass' placeholder='Value class full name')
-            .col-sm-6
-                input.form-control(type='text' ng-model='newKeyClass' ng-focus='field.editIdx = -1' placeholder='Key class full name')
-                .settings-row
-                    input.form-control(type='text' ng-model='newValueClass' ng-focus='field.editIdx = -1' placeholder='Value class full name')
-                button.btn.btn-primary.fieldButton(ng-disabled='!newKeyClass || !newValueClass' ng-click='field.editIdx = -1; addIndexedTypes(newKeyClass, newValueClass)') Add
+            +table-pair('Index key-value type pairs', fieldMdl, 'keyClass', 'valueClass', 'Key class full name', 'Value class full name')