Inserting / deleting records/rows from a non-spatial table

5467
12
Jump to solution
06-06-2016 09:38 AM
NaciDilekli
Occasional Contributor

Hello,

I was already able to edit existing records a non-spatial table in my SDE database using FeatureLayer and FeatureTable following this example (Using FeatureTable (no map) | ArcGIS API for JavaScript) and using the editable property. I would now like to be able to insert (clicking a button would add a blank record at the bottom) and delete selected records from this table. Is this possible? I thought about using applyEdits but I understand it will only accept graphics as parameters (FeatureLayer | API Reference | ArcGIS API for JavaScript)

Thanks!

0 Kudos
1 Solution

Accepted Solutions
BillDaigle
Occasional Contributor III

There is no built in functionality for this (at least at 3.16).  I had a similar need and ended up extending the featureTable dijit and adding the attributeInspector dijit to create a new row.

There is a bunch of extra functionality that you likely won't need, but here is my extended dijit code:

define([
  'dijit/Dialog',
  'dijit/form/Button',
  "dijit/Toolbar",

  "dojo/dom-class",
  'dojo/dom-construct',
  'dojo/_base/array',
  'dojo/_base/declare',
  "dojo/string",
  'dojo/_base/lang',
  'dojo/on',
  "dojo/query",

  'dojo/text!./templates/featureTableEditor.htm',

  'esri/dijit/AttributeInspector',
  'esri/dijit/FeatureTable',
  'esri/layers/FeatureLayer',
  'esri/tasks/query',
  "esri/graphic"
], function(
  Dialog,
  Button,
  Toolbar,

  domClass,
  domConstruct,
  array,
  declare,
  string,
  lang,
  on,
  dojoQuery,

  template,

  AttributeInspector,
  FeatureTable,
  FeatureLayer,
  Query,
  Graphic
) {
  return declare(FeatureTable, {
    constructor: function(params, srcNodeRef) {
      var _this = this;

      this.editableCustom = (params.editable === true) ? true : false;
      params.editable = false;

      this.newRecordAttributes = params.newRecordAttributes || {};
      this.customHeaderText = params.customHeaderText || null;


    },

    buildRendering: function () {

        this.templateString = template;
        this.inherited(arguments);
    },

    postCreate: function() {
      var _this = this;

      console.debug('fte',this)
      this.on('load',this._postGridCreated);

      //add the main class
      domClass.add(this.domNode, "fwijitsFeatureTable");      this._i18nStrings.loadingData = "Loading Table Data...";      this._i18nStrings.gridHeader = '${gridTitle} (${featureSelectedCount}/${featureCount} selected)';
      this.inherited(arguments);
    },


    /////////////////
    //  Functions that override/enhance functionality in the ESRI featureTable
    _refreshGrid: function(ids){
      //forces a refresh of the dataStore item
      var _this = this;
      this._createStoreFromDataQuery();
      var listener = this.grid.on('dgrid-refresh-complete',function(){
        listener.remove();
        if (ids){
          _this.selectRows(ids);
        }

      });
    },
    //override the applyEditsListener
    _applyEditsListener: function() {
      var listener = on(this.featureLayer, "edits-complete", lang.hitch(this, function(listener) {
        this.emit("edits-complete", listener);
      }));
      return listener;
    },
    // End Functions that override/enhance functionality in the ESRI featureTable
    ///////////////////////////////////////////////////////////////////////////


    startup: function() {

      this.inherited(arguments);
    },


    _updateGridHeaderText: function() {
      if (this._gridTitleNode){
        this._gridTitleNode.innerHTML = string.substitute(this._i18nStrings.gridHeader, {
          gridTitle: this.customHeaderText || this.featureLayer.name || this._i18nStrings.untitled,
          featureCount: this.featureCount,
          featureSelectedCount: this._featureSelectedCount
        });
      }
    },

    _postGridCreated: function(){
      var _this = this;

      //open the row editor on double click
      this.grid.on('.dgrid-row:dblclick', function(evt) {
        _this.grid.clearSelection();
        var row = _this.grid.row(evt);
        _this.grid.select(row);
        _this._editSelection();
        //_this._generateRowEditor(row.id);
        //_this._toggleLoadingIndicator('block');
      });


    //  this._setupEditorButtons();

      this.on('row-select',function(row){
        _this._updateEditorButtons('select');
      });
      this.on('row-deselect',function(row){
        _this._updateEditorButtons('deselect');
      });

      //this._addEditorButtons();
    },

    _editSelection: function(){
      //currently only handles single row
      this._generateRowEditor(this.selectedRowIds[0]);
      this._toggleLoadingIndicator('block');
    },

    //_deleteSelection: function(){
    //
    //},

    _updateEditorButtons: function(type){
      console.debug('running',this.selectedRowIds.length,type)
      var disableButtons = false;
      if (type == 'deselect' && this.selectedRowIds.length <= 1){
        disableButtons = true;
      }

      this._editSelectionButton.setDisabled(disableButtons);
      //this._deleteSelectionButton.setDisabled(disableButtons);
    },


    _addNew: function() {
      var _this = this;
      this._toggleLoadingIndicator('block');
      this.featureLayer.applyEdits(
        [{
          'attributes': _this.newRecordAttributes
        }],
        null,
        null,
        function(results) {
          _this._refreshGrid([results[0].objectId]);
          _this._generateRowEditor(results[0].objectId);
        },
        function(err) {
          alert('the add did not work');
        }
      );
    },


    // _addEditorButtons: function() {
    //  var _this = this;
    //
    //  var addnewButtonContainer = domConstruct.create("div", {
    //    class: 'esri-feature-table-menu-item',
    //    style: 'float:left'
    //  }, this.gridMenuAnchor.domNode, "after");
    //
    //  var addnewButton = new Button({
    //    label: "Add New"
    //  }).placeAt(addnewButtonContainer);
    //
    //  addnewButton.on('click', function() {
    //    _this._toggleLoadingIndicator('block');
    //    _this.featureLayer.applyEdits(
    //      [{
    //        'attributes': _this.newRecordAttributes
    //      }],
    //      null,
    //      null,
    //      function(results) {
    //        _this._refreshGrid([results[0].objectId]);
    //        _this._generateRowEditor(results[0].objectId);
    //      },
    //      function(err) {
    //        alert('the add did not work');
    //      }
    //    );
    //  });
    // },

    _generateRowEditor: function(rowId) {

      var _this = this;

      var changeToCommit = false;

      var editFields = [];
      array.forEach(this.featureLayer.fields, function(field) {
        if (_this.hiddenFields.indexOf(field.name) == -1) {
          editFields.push({
            fieldName: field.name,
            isEditable: true,
            label: field.alias
          });
        }
      });

      var attInspector = new AttributeInspector({
        layerInfos: [{
          'featureLayer': this.featureLayer,
          'isEditable': true,
          'fieldInfos': editFields,
          showDeleteButton: false // a custom delete button will be added later
        }]
      }, domConstruct.create("div"));
      domClass.add(attInspector.domNode, "fwijitsFeatureTableAttInspector");

console.debug(attInspector)
      if (_this.saveButton) {
        _this.saveButton.destroy();
      }

      if (_this.deleteButton) {
        _this.deleteButton.destroy();
      }

      if (this.attrInspDialog) {
        _this.attrInspDialog.destroy();
      }

      var rowQuery = new Query();
      rowQuery.where = "ObjectID =" + rowId;

      _this.featureLayer.selectFeatures(rowQuery, FeatureLayer.SELECTION_NEW, function(features) {
        var updateFeature = features[0];
        _this.attrInspDialog = new Dialog({
          title: "Update row",
          content: attInspector.domNode,
          style: "width: 350px",
          class:_this.css.dialog,
          onHide: function() {
            _this.saveButton.destroy();
            _this._toggleLoadingIndicator();
            //_this.clearSelection();
          }
        });
        _this.attrInspDialog.show();

        //add a delete button
        _this.deleteButton = new Button({
          label: "Delete"
        }, domConstruct.create("div"));
        domConstruct.place(_this.deleteButton.domNode, attInspector.deleteBtn.domNode, "after");
        _this.deleteButton.on("click", function() {
          //alert('delete now')
          var apply = _this.featureLayer.applyEdits(
            null,
            null, [updateFeature]
          );
          apply.then(function(res) {
            _this._refreshGrid();
          },
          function(err) {
            alert('delete failed');
          });
          _this.attrInspDialog.hide();

        });

        //add a save button
        _this.saveButton = new Button({
          label: "Save",
          disabled: false
        }, domConstruct.create("div"));
        domConstruct.place(_this.saveButton.domNode, attInspector.deleteBtn.domNode, "after");
        _this.saveButton.on("click", function() {
          _this.attrInspDialog.hide();
          if (changeToCommit === true){
          updateFeature.getLayer().applyEdits(
            null, [updateFeature],
            null,
            function(res) {
              _this._refreshGrid([rowId]);
            },
            function(err) {
              alert('the update did not work');
            }
            );
          }

        });

        attInspector.on("attribute-change", function(attr) {
          //_this.saveButton.setDisabled(false);
          changeToCommit = true;
          updateFeature.attributes[attr.fieldName] = attr.fieldValue;
        });

      });
    },

    //this is a work in progress
    resizeColumnsToContent: function(newSize) {

      for (var i in this.grid.columns) {
        var col = this.grid.columns;
        if (col.hidden == false){
          var fieldName = col.field;
          var fieldAlias = col.label;
          var newWidth = this._getColumnWidth(fieldName,fieldAlias);
          this.grid.resizeColumnWidth(col.id, newWidth);
        }

      }
    },

    _stripTags: function(HTML) {
      var tmp = document.createElement("DIV");
      tmp.innerHTML = HTML;
      return tmp.textContent || tmp.innerText;
    },

    _replaceURLWithHTMLLinks: function(text) {
      var t = text + "";
      var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
      return t.replace(exp, "<a href='$1' target='blank'>View Page</a>");
    },

    _getColumnWidth: function(fieldName, fieldAlias, data) {
      var _this = this;
      var longestDataField = "-";

      //fetch the field domain
      var domain = null;
      array.forEach(this.featureLayer.fields,function(fieldInfo){
        if (fieldInfo.name == fieldName && fieldInfo.domain){
          domain = fieldInfo.domain;
        }
      });

      array.forEach(this.grid.store.data, function(item) {
        console.debug('  ',item)
        var cellData;
        if (item.attributes) {
          cellData = item.attributes[fieldName];
        } else {
          cellData = item[fieldName];
        }

        //handle domains
        if (domain && domain.codedValues){
          var cellText = '';
          array.forEach(domain.codedValues,function(codedValue){
            if (codedValue.code == cellData){
              cellText = codedValue.name;
            }
          });
          if (cellText !== ''){
            cellData = cellText;
          }
        }
        console.debug('  ',cellData)
        //account for empty cells
        cellData = (!cellData || cellData === null || cellData === ' ' || cellData === '') ? "-" : cellData;

        //Strip any html tags
        if (typeof cellData === 'string' && cellData.indexOf('<a') > -1) {
          cellData = _this._stripTags(cellData);
        } else if (typeof cellData === 'string' && cellData.indexOf('http') > -1 && cellData.indexOf('<a') == -1) {
          cellData = _this._stripTags(_this._replaceURLWithHTMLLinks(cellData));
        }
        //check to see if the current fld is longer than previous flds
        if (cellData.length > longestDataField.length) {
          longestDataField = cellData;
        }
      });

      //calculate header and cell string length
      var resizeFactor = 1.5;
      var maxColumnWidth = 30;

      var cellLen = longestDataField.length / resizeFactor;
      var hdrLen = fieldAlias.length <= 10 ? fieldAlias.length : fieldAlias.length / 1.3;
      var columnWidth = hdrLen > cellLen ? hdrLen : cellLen;
      columnWidth = columnWidth < 30 ? columnWidth : maxColumnWidth;
      return columnWidth*10;
    }
  });
});

The template:

<div class="${baseClass}">
  <div id="${gridBorderContainerId}" class="esri-feature-table-border-container" data-dojo-attach-point="_gridBorderContainer" data-dojo-type="dijit.layout.BorderContainer" gutters="false">
    <div id="${gridHeaderId}" class="esri-feature-table-content-pane esri-feature-table-menu" data-dojo-attach-point="_gridHeaderNode" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'top'">
      <div class="esri-feature-table-menu-item esri-feature-table-loading-indicator" data-dojo-attach-point="_gridLoadingIndicatorNode" style="display:none;"></div>
      <div class="esri-feature-table-menu-item esri-feature-table-title" data-dojo-attach-point="_gridTitleNode"></div>
      <div class="esri-feature-table-menu-item esri-feature-table-closer-container">
        <a class="esri-feature-table-closer toggleOpened" data-dojo-attach-point="tableCloseButton" href="JavaScript:void(0);" title="Hide Table"></a>
      </div>
      <div data-dojo-attach-point="_menuNode" class="esri-feature-table-menu-item esri-feature-table-menu-options">
        <div id="${gridMenuNodeId}"></div>
      </div>
    </div>
    <div id="${gridContentPaneId}" class="esri-feature-table-content-pane" data-dojo-attach-point="_gridContentPane" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'center'">
      <div id="${gridId}" class="esri-feature-table-grid" data-dojo-attach-point="_gridNode"></div>
    </div>
    <div class="esri-feature-table-editor-toolbar" data-dojo-attach-point="_gridEditToolbarPane" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'bottom'">
      <div  data-dojo-attach-point="_gridEditorToolbar" data-dojo-type="dijit/Toolbar">
        <div data-dojo-type="dijit/form/Button" data-dojo-attach-event="onClick:_addNew" data-dojo-props="iconClass:'fwijitsFeatureTableEditorIcon fwijitsFeatureTableAdd', showLabel:false">Add</div>
        <div data-dojo-type="dijit/form/Button" data-dojo-attach-point="_editSelectionButton" data-dojo-attach-event="onClick:_editSelection" data-dojo-props="iconClass:'fwijitsFeatureTableEditorIcon fwijitsFeatureTableEdit', showLabel:false, disabled:true">Edit Selected</div>
        <!--div data-dojo-type="dijit/form/Button" data-dojo-attach-point="_deleteSelectionButton" data-dojo-attach-event="onClick:_deleteSelection" data-dojo-props="iconClass:'fwijitsFeatureTableEditorIcon fwijitsFeatureTableDelete', showLabel:false, disabled:true">Delete Selected</div-->
      </div>
    </div>
  </div>

</div>

The css:

.dgrid {
    font-size: 9px;
}
.fwijitsFeatureTableMapSelectedRows .dgrid-selected {
    background-color: #00ffff;
}

.fwijitsFeatureTableMapSelectedRows .dgrid-row:hover {
    border: 2px solid #ff00ff;
    background-color: transparent;
}
.fwijitsFeatureTableMapSelectedRows .dgrid-selected:hover {
    border: 2px solid #ff00ff;
    background-color: #00ffff;
}

.fwijitsFeatureTableAttInspector .atiLayerName {
  display:none;
}

.esri-feature-table-editor-toolbar{

}

.fwijitsFeatureTable .esri-feature-table-closer-container{
  display:none;
}

.fwijitsFeatureTableAttInspector .atiAttributes .dijitButtonNode{
  border-radius:0px !important;
}
.fwijitsFeatureTableAttInspector .atiField{
  padding:0px !important;
  margin:2px;
}

.fwijitsFeatureTableEditorIcon {
    background-image: url('images/featureTableEditor/editorIconsEnabled.png'); /* editor icons sprite image for the enabled state */
    background-repeat: no-repeat;
    width: 18px;
    height: 18px;
    text-align: center;
}
.dijitDisabled .fwijitsFeatureTableEditorIcon {
    background-image: url('images/featureTableEditor/editorIconsDisabled.png'); /* editor icons sprite image for the disabled state */
}

.fwijitsFeatureTableAdd { background-position: 0; }
.fwijitsFeatureTableEdit { background-position: -18px; }
.fwijitsFeatureTableDelete { background-position: -36px; }

At the very least, the javascript code should give you a start.

View solution in original post

12 Replies
AbbyGallegos
New Contributor II

I have this same question.  I did not see any way to do this using the FeatureTable.  Does anyone have any hints or suggestions as to accomplish adding a new row to the FeatureTable (and to delete an existing row)?

Thanks,

Abby

0 Kudos
thejuskambi
Occasional Contributor III

Have you tried creating a new Graphic object and not update the geometry?

0 Kudos
BillDaigle
Occasional Contributor III

There is no built in functionality for this (at least at 3.16).  I had a similar need and ended up extending the featureTable dijit and adding the attributeInspector dijit to create a new row.

There is a bunch of extra functionality that you likely won't need, but here is my extended dijit code:

define([
  'dijit/Dialog',
  'dijit/form/Button',
  "dijit/Toolbar",

  "dojo/dom-class",
  'dojo/dom-construct',
  'dojo/_base/array',
  'dojo/_base/declare',
  "dojo/string",
  'dojo/_base/lang',
  'dojo/on',
  "dojo/query",

  'dojo/text!./templates/featureTableEditor.htm',

  'esri/dijit/AttributeInspector',
  'esri/dijit/FeatureTable',
  'esri/layers/FeatureLayer',
  'esri/tasks/query',
  "esri/graphic"
], function(
  Dialog,
  Button,
  Toolbar,

  domClass,
  domConstruct,
  array,
  declare,
  string,
  lang,
  on,
  dojoQuery,

  template,

  AttributeInspector,
  FeatureTable,
  FeatureLayer,
  Query,
  Graphic
) {
  return declare(FeatureTable, {
    constructor: function(params, srcNodeRef) {
      var _this = this;

      this.editableCustom = (params.editable === true) ? true : false;
      params.editable = false;

      this.newRecordAttributes = params.newRecordAttributes || {};
      this.customHeaderText = params.customHeaderText || null;


    },

    buildRendering: function () {

        this.templateString = template;
        this.inherited(arguments);
    },

    postCreate: function() {
      var _this = this;

      console.debug('fte',this)
      this.on('load',this._postGridCreated);

      //add the main class
      domClass.add(this.domNode, "fwijitsFeatureTable");      this._i18nStrings.loadingData = "Loading Table Data...";      this._i18nStrings.gridHeader = '${gridTitle} (${featureSelectedCount}/${featureCount} selected)';
      this.inherited(arguments);
    },


    /////////////////
    //  Functions that override/enhance functionality in the ESRI featureTable
    _refreshGrid: function(ids){
      //forces a refresh of the dataStore item
      var _this = this;
      this._createStoreFromDataQuery();
      var listener = this.grid.on('dgrid-refresh-complete',function(){
        listener.remove();
        if (ids){
          _this.selectRows(ids);
        }

      });
    },
    //override the applyEditsListener
    _applyEditsListener: function() {
      var listener = on(this.featureLayer, "edits-complete", lang.hitch(this, function(listener) {
        this.emit("edits-complete", listener);
      }));
      return listener;
    },
    // End Functions that override/enhance functionality in the ESRI featureTable
    ///////////////////////////////////////////////////////////////////////////


    startup: function() {

      this.inherited(arguments);
    },


    _updateGridHeaderText: function() {
      if (this._gridTitleNode){
        this._gridTitleNode.innerHTML = string.substitute(this._i18nStrings.gridHeader, {
          gridTitle: this.customHeaderText || this.featureLayer.name || this._i18nStrings.untitled,
          featureCount: this.featureCount,
          featureSelectedCount: this._featureSelectedCount
        });
      }
    },

    _postGridCreated: function(){
      var _this = this;

      //open the row editor on double click
      this.grid.on('.dgrid-row:dblclick', function(evt) {
        _this.grid.clearSelection();
        var row = _this.grid.row(evt);
        _this.grid.select(row);
        _this._editSelection();
        //_this._generateRowEditor(row.id);
        //_this._toggleLoadingIndicator('block');
      });


    //  this._setupEditorButtons();

      this.on('row-select',function(row){
        _this._updateEditorButtons('select');
      });
      this.on('row-deselect',function(row){
        _this._updateEditorButtons('deselect');
      });

      //this._addEditorButtons();
    },

    _editSelection: function(){
      //currently only handles single row
      this._generateRowEditor(this.selectedRowIds[0]);
      this._toggleLoadingIndicator('block');
    },

    //_deleteSelection: function(){
    //
    //},

    _updateEditorButtons: function(type){
      console.debug('running',this.selectedRowIds.length,type)
      var disableButtons = false;
      if (type == 'deselect' && this.selectedRowIds.length <= 1){
        disableButtons = true;
      }

      this._editSelectionButton.setDisabled(disableButtons);
      //this._deleteSelectionButton.setDisabled(disableButtons);
    },


    _addNew: function() {
      var _this = this;
      this._toggleLoadingIndicator('block');
      this.featureLayer.applyEdits(
        [{
          'attributes': _this.newRecordAttributes
        }],
        null,
        null,
        function(results) {
          _this._refreshGrid([results[0].objectId]);
          _this._generateRowEditor(results[0].objectId);
        },
        function(err) {
          alert('the add did not work');
        }
      );
    },


    // _addEditorButtons: function() {
    //  var _this = this;
    //
    //  var addnewButtonContainer = domConstruct.create("div", {
    //    class: 'esri-feature-table-menu-item',
    //    style: 'float:left'
    //  }, this.gridMenuAnchor.domNode, "after");
    //
    //  var addnewButton = new Button({
    //    label: "Add New"
    //  }).placeAt(addnewButtonContainer);
    //
    //  addnewButton.on('click', function() {
    //    _this._toggleLoadingIndicator('block');
    //    _this.featureLayer.applyEdits(
    //      [{
    //        'attributes': _this.newRecordAttributes
    //      }],
    //      null,
    //      null,
    //      function(results) {
    //        _this._refreshGrid([results[0].objectId]);
    //        _this._generateRowEditor(results[0].objectId);
    //      },
    //      function(err) {
    //        alert('the add did not work');
    //      }
    //    );
    //  });
    // },

    _generateRowEditor: function(rowId) {

      var _this = this;

      var changeToCommit = false;

      var editFields = [];
      array.forEach(this.featureLayer.fields, function(field) {
        if (_this.hiddenFields.indexOf(field.name) == -1) {
          editFields.push({
            fieldName: field.name,
            isEditable: true,
            label: field.alias
          });
        }
      });

      var attInspector = new AttributeInspector({
        layerInfos: [{
          'featureLayer': this.featureLayer,
          'isEditable': true,
          'fieldInfos': editFields,
          showDeleteButton: false // a custom delete button will be added later
        }]
      }, domConstruct.create("div"));
      domClass.add(attInspector.domNode, "fwijitsFeatureTableAttInspector");

console.debug(attInspector)
      if (_this.saveButton) {
        _this.saveButton.destroy();
      }

      if (_this.deleteButton) {
        _this.deleteButton.destroy();
      }

      if (this.attrInspDialog) {
        _this.attrInspDialog.destroy();
      }

      var rowQuery = new Query();
      rowQuery.where = "ObjectID =" + rowId;

      _this.featureLayer.selectFeatures(rowQuery, FeatureLayer.SELECTION_NEW, function(features) {
        var updateFeature = features[0];
        _this.attrInspDialog = new Dialog({
          title: "Update row",
          content: attInspector.domNode,
          style: "width: 350px",
          class:_this.css.dialog,
          onHide: function() {
            _this.saveButton.destroy();
            _this._toggleLoadingIndicator();
            //_this.clearSelection();
          }
        });
        _this.attrInspDialog.show();

        //add a delete button
        _this.deleteButton = new Button({
          label: "Delete"
        }, domConstruct.create("div"));
        domConstruct.place(_this.deleteButton.domNode, attInspector.deleteBtn.domNode, "after");
        _this.deleteButton.on("click", function() {
          //alert('delete now')
          var apply = _this.featureLayer.applyEdits(
            null,
            null, [updateFeature]
          );
          apply.then(function(res) {
            _this._refreshGrid();
          },
          function(err) {
            alert('delete failed');
          });
          _this.attrInspDialog.hide();

        });

        //add a save button
        _this.saveButton = new Button({
          label: "Save",
          disabled: false
        }, domConstruct.create("div"));
        domConstruct.place(_this.saveButton.domNode, attInspector.deleteBtn.domNode, "after");
        _this.saveButton.on("click", function() {
          _this.attrInspDialog.hide();
          if (changeToCommit === true){
          updateFeature.getLayer().applyEdits(
            null, [updateFeature],
            null,
            function(res) {
              _this._refreshGrid([rowId]);
            },
            function(err) {
              alert('the update did not work');
            }
            );
          }

        });

        attInspector.on("attribute-change", function(attr) {
          //_this.saveButton.setDisabled(false);
          changeToCommit = true;
          updateFeature.attributes[attr.fieldName] = attr.fieldValue;
        });

      });
    },

    //this is a work in progress
    resizeColumnsToContent: function(newSize) {

      for (var i in this.grid.columns) {
        var col = this.grid.columns;
        if (col.hidden == false){
          var fieldName = col.field;
          var fieldAlias = col.label;
          var newWidth = this._getColumnWidth(fieldName,fieldAlias);
          this.grid.resizeColumnWidth(col.id, newWidth);
        }

      }
    },

    _stripTags: function(HTML) {
      var tmp = document.createElement("DIV");
      tmp.innerHTML = HTML;
      return tmp.textContent || tmp.innerText;
    },

    _replaceURLWithHTMLLinks: function(text) {
      var t = text + "";
      var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
      return t.replace(exp, "<a href='$1' target='blank'>View Page</a>");
    },

    _getColumnWidth: function(fieldName, fieldAlias, data) {
      var _this = this;
      var longestDataField = "-";

      //fetch the field domain
      var domain = null;
      array.forEach(this.featureLayer.fields,function(fieldInfo){
        if (fieldInfo.name == fieldName && fieldInfo.domain){
          domain = fieldInfo.domain;
        }
      });

      array.forEach(this.grid.store.data, function(item) {
        console.debug('  ',item)
        var cellData;
        if (item.attributes) {
          cellData = item.attributes[fieldName];
        } else {
          cellData = item[fieldName];
        }

        //handle domains
        if (domain && domain.codedValues){
          var cellText = '';
          array.forEach(domain.codedValues,function(codedValue){
            if (codedValue.code == cellData){
              cellText = codedValue.name;
            }
          });
          if (cellText !== ''){
            cellData = cellText;
          }
        }
        console.debug('  ',cellData)
        //account for empty cells
        cellData = (!cellData || cellData === null || cellData === ' ' || cellData === '') ? "-" : cellData;

        //Strip any html tags
        if (typeof cellData === 'string' && cellData.indexOf('<a') > -1) {
          cellData = _this._stripTags(cellData);
        } else if (typeof cellData === 'string' && cellData.indexOf('http') > -1 && cellData.indexOf('<a') == -1) {
          cellData = _this._stripTags(_this._replaceURLWithHTMLLinks(cellData));
        }
        //check to see if the current fld is longer than previous flds
        if (cellData.length > longestDataField.length) {
          longestDataField = cellData;
        }
      });

      //calculate header and cell string length
      var resizeFactor = 1.5;
      var maxColumnWidth = 30;

      var cellLen = longestDataField.length / resizeFactor;
      var hdrLen = fieldAlias.length <= 10 ? fieldAlias.length : fieldAlias.length / 1.3;
      var columnWidth = hdrLen > cellLen ? hdrLen : cellLen;
      columnWidth = columnWidth < 30 ? columnWidth : maxColumnWidth;
      return columnWidth*10;
    }
  });
});

The template:

<div class="${baseClass}">
  <div id="${gridBorderContainerId}" class="esri-feature-table-border-container" data-dojo-attach-point="_gridBorderContainer" data-dojo-type="dijit.layout.BorderContainer" gutters="false">
    <div id="${gridHeaderId}" class="esri-feature-table-content-pane esri-feature-table-menu" data-dojo-attach-point="_gridHeaderNode" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'top'">
      <div class="esri-feature-table-menu-item esri-feature-table-loading-indicator" data-dojo-attach-point="_gridLoadingIndicatorNode" style="display:none;"></div>
      <div class="esri-feature-table-menu-item esri-feature-table-title" data-dojo-attach-point="_gridTitleNode"></div>
      <div class="esri-feature-table-menu-item esri-feature-table-closer-container">
        <a class="esri-feature-table-closer toggleOpened" data-dojo-attach-point="tableCloseButton" href="JavaScript:void(0);" title="Hide Table"></a>
      </div>
      <div data-dojo-attach-point="_menuNode" class="esri-feature-table-menu-item esri-feature-table-menu-options">
        <div id="${gridMenuNodeId}"></div>
      </div>
    </div>
    <div id="${gridContentPaneId}" class="esri-feature-table-content-pane" data-dojo-attach-point="_gridContentPane" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'center'">
      <div id="${gridId}" class="esri-feature-table-grid" data-dojo-attach-point="_gridNode"></div>
    </div>
    <div class="esri-feature-table-editor-toolbar" data-dojo-attach-point="_gridEditToolbarPane" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'bottom'">
      <div  data-dojo-attach-point="_gridEditorToolbar" data-dojo-type="dijit/Toolbar">
        <div data-dojo-type="dijit/form/Button" data-dojo-attach-event="onClick:_addNew" data-dojo-props="iconClass:'fwijitsFeatureTableEditorIcon fwijitsFeatureTableAdd', showLabel:false">Add</div>
        <div data-dojo-type="dijit/form/Button" data-dojo-attach-point="_editSelectionButton" data-dojo-attach-event="onClick:_editSelection" data-dojo-props="iconClass:'fwijitsFeatureTableEditorIcon fwijitsFeatureTableEdit', showLabel:false, disabled:true">Edit Selected</div>
        <!--div data-dojo-type="dijit/form/Button" data-dojo-attach-point="_deleteSelectionButton" data-dojo-attach-event="onClick:_deleteSelection" data-dojo-props="iconClass:'fwijitsFeatureTableEditorIcon fwijitsFeatureTableDelete', showLabel:false, disabled:true">Delete Selected</div-->
      </div>
    </div>
  </div>

</div>

The css:

.dgrid {
    font-size: 9px;
}
.fwijitsFeatureTableMapSelectedRows .dgrid-selected {
    background-color: #00ffff;
}

.fwijitsFeatureTableMapSelectedRows .dgrid-row:hover {
    border: 2px solid #ff00ff;
    background-color: transparent;
}
.fwijitsFeatureTableMapSelectedRows .dgrid-selected:hover {
    border: 2px solid #ff00ff;
    background-color: #00ffff;
}

.fwijitsFeatureTableAttInspector .atiLayerName {
  display:none;
}

.esri-feature-table-editor-toolbar{

}

.fwijitsFeatureTable .esri-feature-table-closer-container{
  display:none;
}

.fwijitsFeatureTableAttInspector .atiAttributes .dijitButtonNode{
  border-radius:0px !important;
}
.fwijitsFeatureTableAttInspector .atiField{
  padding:0px !important;
  margin:2px;
}

.fwijitsFeatureTableEditorIcon {
    background-image: url('images/featureTableEditor/editorIconsEnabled.png'); /* editor icons sprite image for the enabled state */
    background-repeat: no-repeat;
    width: 18px;
    height: 18px;
    text-align: center;
}
.dijitDisabled .fwijitsFeatureTableEditorIcon {
    background-image: url('images/featureTableEditor/editorIconsDisabled.png'); /* editor icons sprite image for the disabled state */
}

.fwijitsFeatureTableAdd { background-position: 0; }
.fwijitsFeatureTableEdit { background-position: -18px; }
.fwijitsFeatureTableDelete { background-position: -36px; }

At the very least, the javascript code should give you a start.

NaciDilekli
Occasional Contributor

Thank you very much. I am trying to first use your code by integrating into the example at Using FeatureTable (no map) | ArcGIS API for JavaScript 3.17 , but so far I have been unsuccessful. I am probably doing some very basic mistakes but here is what I am doing. Here is my version of code (in a file text.html) and the folder structure.

<!DOCTYPE html>
<html>


<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>FeatureTable without map</title>
  <link rel="stylesheet" href="https://community.esri.com//js.arcgis.com/3.17/dijit/themes/claro/claro.css">
  <link rel="stylesheet" href="https://community.esri.com//js.arcgis.com/3.17/esri/css/esri.css">
  <link rel="stylesheet" href="https://community.esri.com//js.arcgis.com/3.17/esri/css/esri.css">
  <script src="//js.arcgis.com/3.17/"></script>
  <script type="text/javascript">
  dojoConfig = {
    parseOnLoad: true,
    packages: [{
    "name": "newModule",
    "location": location.pathname.replace(/\/[^/]+$/, "") + "/module"
  }]
};  

  </script>
  
  <style>
    html, 
    body, 
    #myTableNode {
      width:100%;
      height:100%;
      margin:0;
      padding:0;
    }
    
    #top, 
    #bot {
      margin: 0;
      padding: 0;
    }
  </style>


  <script>
    var map;
    //console.log(location.pathname.replace(/\/[^/]+$/, "") + "/module");


    require([
      "esri/layers/FeatureLayer",
      "esri/dijit/FeatureTable",
      "newModule/featureTableEditor",


      "dojo/dom",
      "dojo/parser",
      "dojo/ready",
    ], function (
      FeatureLayer, FeatureTable,featureTableEditor,
      dom, parser, ready    ) {


      parser.parse();


      ready(function(){


        // Create the feature layer
        var myFeatureLayer = new FeatureLayer("https://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/california_census_blocks/FeatureSe...", {
          mode: FeatureLayer.MODE_ONDEMAND,
          outFields:  ["NAME","GEOID","MTFCC","ALAND","AWATER"],
          visible: true,
          id: "fLayer"
        });


        myTable = new featureTableEditor({
          featureLayer : myFeatureLayer,
          showGridMenu: false,
          hiddenFields: ["FID","C_Seq","Street"]  // field that end-user can show, but is hidden on startup
        }, "myTableNode");


        myTable.startup();
      });
    });
  </script>
</head>


<body class="claro esri">
  <div data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design:'headline', gutters: false" style="width:100%; height:100%;">
    <div id="top" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'top'">
    <p><em>The FeatureTable dijit supports tables with lots of features, with the table growing as you scroll.</em></p>
    </div>
    <div id="bot" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'center'">
      <div id="myTableNode"></div>
    </div>
  </div>
</body>


</html> 


















folderstructure.png

0 Kudos
BillDaigle
Occasional Contributor III

I don't think your too far off.  Here are a couple things I notice right off the bat...

  • I haven't tested it with 3.17.  I did override some private functions, so there may be breaking changes at 3.17. It should work with 3.16.
  • You will need to add a link to the stylesheet -- the dijit won't load it by default.
  • Also, attached are the 2 sprite images referenced in the stylesheet.
NaciDilekli
Occasional Contributor

Thanks. I got it working now. It looks and works awesome. By the way the main reason it didn't work was because I wasn't aware of "dojo/text!" requirement. I was putting "templates/featureTableEditor.html" for importing and the compiler was naturally looking for a JS file.

Thanks again!!

0 Kudos
BillDaigle
Occasional Contributor III

Excellent. Glad to hear it works for you. Were you able to test it on 3.17?

0 Kudos
NaciDilekli
Occasional Contributor

I've just tried, and it doesn't work. I am getting this error:

Error: uniqName_0 template:gridBorderContainerId

Stack trace:

.cache["dijit/_TemplatedMixin"]/</e<._stringRepl/<@https://js.arcgis.com/3.17/:372:222

.cache["dojo/_base/lang"]/</e.hitch/<@https://js.arcgis.com/3.17/:63:207

.cache["dojo/string"]/</q.substitute/<@https://js.arcgis.com/3.17/:376:308

.cache["dojo/string"]/</q.substitute@https://js.arcgis.com/3.17/:376:154

.cache["dijit/_TemplatedMixin"]/</e<._stringRepl@https://js.arcgis.com/3.17/:372:105

.cache["dijit/_TemplatedMixin"]/</e<.buildRendering@https://js.arcgis.com/3.17/:373:177

m@https://js.arcgis.com/3.17/:155:342

.buildRendering@http://127.0.0.1:8020/extendFeatureTable/module/featureTableEditor.js:60:9

.cache["dijit/_WidgetBase"]/</f<.create@https://js.arcgis.com/3.17/:350:13

.cache["dijit/_WidgetBase"]/</f<.postscript@https://js.arcgis.com/3.17/:348:470

d/<@https://js.arcgis.com/3.17/:157:442

@http://127.0.0.1:8020/extendFeatureTable/test.html:68:19

.cache["dojo/ready"]/</b.addOnLoad/f<@https://js.arcgis.com/3.17/:150:250

.cache["dojo/ready"]/</e@https://js.arcgis.com/3.17/:149:503

q.signal/<@https://js.arcgis.com/3.17/:14:445

a@https://js.arcgis.com/3.17/:10:295

q.signal@https://js.arcgis.com/3.17/:14:417

ja@https://js.arcgis.com/3.17/:28:166

ga@https://js.arcgis.com/3.17/:28:198

qa/s@https://js.arcgis.com/3.17/:30:434

q.injectUrl/g<@https://js.arcgis.com/3.17/:35:149

3.17:36:486

I have a hard time reading these errors. I suppose the error is triggered by the "this.inherited(arguments);" line in the buildRendering function inside featureTableEditor   Can you tell what this is about?

0 Kudos
BillDaigle
Occasional Contributor III

It looks like they modified the template quite a bit at 3.17.  Try using the following html.  I haven't tried this myself yet.

<div>
  <div class="esri-feature-table-border-container" data-dojo-attach-point= "_gridBorderContainer" data-dojo-type="dijit.layout.BorderContainer" gutters="false">
    <div class="esri-feature-table-content-pane esri-feature-table-menu" data-dojo-attach-point= "_gridHeaderNode" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'top'">
      <div class="esri-feature-table-menu-item esri-feature-table-loading-indicator" data-dojo-attach-point= "_gridLoadingIndicatorNode" style="display:none;"></div>
      <div class="esri-feature-table-menu-item esri-feature-table-title" data-dojo-attach-point= "_gridTitleNode"></div>
      <div class="esri-feature-table-menu-item esri-feature-table-closer-container"></div>   
      <div data-dojo-attach-point= "_menuNode" class="esri-feature-table-menu-item esri-feature-table-menu-options">
        <div data-dojo-attach-point= "_gridMenuNode"></div>
      </div>
    </div>
    <div class="esri-feature-table-content-pane" data-dojo-attach-point= "_gridContentPane" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'center'">
      <div class="esri-feature-table-grid" data-dojo-attach-point="_gridNode"></div>
    </div>
    <!-- custom editor tools section -->
    <div class="esri-feature-table-editor-toolbar" data-dojo-attach-point="_gridEditToolbarPane" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'bottom'">
      <div  data-dojo-attach-point="_gridEditorToolbar" data-dojo-type="dijit/Toolbar">
        <div data-dojo-type="dijit/form/Button" data-dojo-attach-event="onClick:_addNew" data-dojo-props="iconClass:'fwijitsFeatureTableEditorIcon fwijitsFeatureTableAdd', showLabel:false">Add</div>
        <div data-dojo-type="dijit/form/Button" data-dojo-attach-point="_editSelectionButton" data-dojo-attach-event="onClick:_editSelection" data-dojo-props="iconClass:'fwijitsFeatureTableEditorIcon fwijitsFeatureTableEdit', showLabel:false, disabled:true">Edit Selected</div>
        <!--div data-dojo-type="dijit/form/Button" data-dojo-attach-point="_deleteSelectionButton" data-dojo-attach-event="onClick:_deleteSelection" data-dojo-props="iconClass:'fwijitsFeatureTableEditorIcon fwijitsFeatureTableDelete', showLabel:false, disabled:true">Delete Selected</div-->
      </div>
    </div>
    <!-- end custom editor tools section -->
  </div>
</div>