FeatureTable with RelationshipQuery

4910
8
02-12-2015 04:13 PM
DavidColey
Frequent Contributor

Hi-

Has anyone used the FeatureTable dijit with a RelationshipQuery -  i.e. to return related records?  Maybe the RelationshipQuery has to come from a TableDataSource?

Thanks-

David

0 Kudos
8 Replies
KellyHutchins
Esri Frequent Contributor

David,

I've done this with a dgrid  - should be possible with FeatureTable too but haven't tried. Here's the dgrid example:

<!DOCTYPE html>
  <html>
  <head>
  <title>Display Related Records</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
  <link rel="stylesheet" href="https://community.esri.com//jsdev.arcgis.com/3.13/dgrid/css/dgrid.css">
  <link rel="stylesheet" href="https://community.esri.com//jsdev.arcgis.com/3.13/dgrid/css/skins/claro.css">
  <link rel="stylesheet" href="http://js.arcgis.com/3.12/esri/css/esri.css">
  <style>
    html,body,#mapDiv,.map.container{
      padding:0;
      margin:0;
      height:100%;
    }
    #tableContainer{
      position: absolute;
      z-index: 99;
      bottom:0;
      right:0;
      left:0;
      height: 15%;
    }
  </style>


  <script>var dojoConfig = { parseOnLoad: true };</script>
  <script src="http://js.arcgis.com/3.12"></script>
  <script>
    var map,grid = null,
        webmapId = "283117cc8d5c4028acc30a0609d58254";


        //Note the web map id has related records 
    
    require([
      "esri/map",
      "esri/arcgis/utils",
      "dgrid/OnDemandGrid",
      "dojo/store/Memory",
      "esri/tasks/RelationshipQuery",
      "esri/domUtils",
      "dojo/dom-construct",
      "dojo/query",
      "dojo/_base/array",
      "dojo/_base/window",
      "dijit/registry",
      "dojo/on",
      "dojo/domReady!"
      ], function (Map, arcgisUtils, Grid, Memory, RelationshipQuery, domUtils, domConstruct, query, array, win, registry, on) {
        arcgisUtils.createMap(webmapId, "mapDiv").then(function (response) {
        map = response.map;
        on(map.infoWindow, "hide", function(){
          //close the table if open 
          clearGrid();
        });
        //add a link to the popup that for getting related records 
        var link = domConstruct.create("a",{
          "class": "action", 
          "id": "relatedLink",
          "innerHTML": "Show Related Records", //text that appears in the popup for the link 
          "href": "javascript: void(0);"
        }, query(".actionList", window.map.infoWindow.domNode)[0]);




        on(link, "click", function(){
          //Get info from the selected feature 
          var feature = map.infoWindow.getSelectedFeature();
          var layer = feature.getLayer();
          var OIDField = layer.objectIdField;
          var oids = feature.attributes[OIDField];


          //Setup the relationship query 
          var relatedQuery = new RelationshipQuery();
          relatedQuery.outFields = ["YAPIID", "OPERATIONNUMBER"];
          relatedQuery.relationshipId = 0;
          relatedQuery.objectIds = [oids];


          //Perform the query
          layer.queryRelatedFeatures(relatedQuery, function(relatedRecords){
            //get the related records for the input OID. In this case there
            //is just one object id. 
            clearGrid();
            var featureSet = relatedRecords[oids];
            if(featureSet && featureSet.features){
             createTable(featureSet);
            }else{
              alert("No related records");
            }
          });


        });
    });


      function clearGrid(){
        //clear existing grid 
        if(grid){
            grid.set("store",new Memory({data:[]}));
            domUtils.hide(grid.domNode);
        }
      }
      function createTable(featureSet, info){
        var data = [];      
        array.forEach(featureSet.features, function(r){
          data.push(r.attributes);
        });
        //define the columns


        var columns = {
          "YAPIID": "ID",
          "OPERATIONNUMBER": "Op Number"
        };
        var memoryStore =  new Memory({data: data});


        grid = new Grid({
          columns: columns
        },"tableContainer");




        grid.set("store", memoryStore);
        domUtils.show(grid.domNode);
      }
    });
  </script>


  </head>


  <body class="claro">
    <div id="mapDiv"></div>
    <div id="tableContainer"></div>
  </body>
  </html>
0 Kudos
DavidColey
Frequent Contributor

Hi Kelly, thanks.  I'm bascially already doing the same thing wih a parcel - property 1:m (not showing require modules and aliases):

relGrid = new (declare([Grid, ColumnSet, DijitRegistry, ColumnResizer]))({
  //minRowsPerPage: 500,
                bufferRows : Infinity,
                //selectionMode: "single",
                loadingMessage: 'Loading data...',
                noDataMessage : "No Records Available",
                useTouchScroll: false,
                columnSets : [
                [
                     [
                          {field: 'ID', label: 'Account', resizable: false},
                          {field: 'LINK', label: 'Information', formatter : function(link) {return "<a target='_blank' href=" + link + ">Full Info</a>";}, resizable: false}
                     ]
               
                ],[
                          [
                          {field: 'NAME1', label: 'Owner Name', resizable: false},
                          {field: 'JUST', label: 'Justified Value', formatter: toCurrency, resizable: false},
                          {field: 'ASSD', label: 'Assessed Value', formatter: toCurrency, resizable: false},
                          {field: 'YRBL', label: 'Year Built', resizable: false}
                          //{field: 'LSQFT', label: 'Recorded Area', resizable: false}
                          ]
                     ]
                ]
    }, "divGrid");
    //relGrid.startup();

function getLocation(evt) {
       mapMain.graphics.clear();
       mPoint = evt.mapPoint; //evt.
       console.log(evt);
       var queryParcels = new Query;
             queryParcels.geometry =  mPoint; //mPo
             var parQuery = lyrParcels.selectFeatures(queryParcels,FeatureLayer.SELECTION_NEW); //deferred evacQuery
             console.log(mPoint);

        mapMain.infoWindow.setFeatures([parQuery]);  //mapMain.infoWindow.setContent(evt.result.name);
        mapMain.infoWindow.show(mPoint); ///evt.result.feature.geometry);
if (mPoint !== undefined) {
  mapMain.centerAndZoom(mPoint, 13); //7 for sph mPoint 13
  }

lyrParcels.on("selection-complete", findRelatedRecords);

function findRelatedRecords(event) {
    features = event.features;
        relatedParQuery = new RelationshipQuery();
            relatedParQuery.outFields = ["OBJECTID","ID", "NAME1", "JUST", "ASSD", "YRBL", "LSQFT"];
            relatedParQuery.relationshipId = 1;
            relatedParQuery.objectIds = [features[0].attributes.OBJECTID];


lyrParcels.queryRelatedFeatures(relatedParQuery, function(relatedRecords) {

if (!relatedRecords.hasOwnProperty(features[0].attributes.OBJECTID) ) {
            console.log("No related records for ObjectID: ", features[0].attributes.OBJECTID);
            return;
              }          
            fset = relatedRecords[features[0].attributes.OBJECTID];
for (var i=0 ; i < fset.features.length; i++) {
            fset.features.attributes.LINK = "http://www.sc-pa.com/testsearch/parcel/" + fset.features.attributes.ID;
            }
        console.log(fset);

items = array.map(fset.features, function(feature) { //
                return feature.attributes;
            });
            
            for (var i=0 ; i < items.length; i++) {
              items.JUST = parseInt(items.JUST); //parses numeric integer (parseInt) and float (parseFloat) into an real number
          }
          for (var i=0 ; i < items.length; i++) {
              items.ASSD = parseInt(items.ASSD);
          }
          for (var i=0 ; i < items.length; i++) {
              items.LSQFT = parseInt(items.LSQFT);
          }
              //Create data object to be used in store
            relData = {
               identifier: "OBJECTID",  //This field needs to have unique values
               label: "OBJECTID", //Name field for display. Not pertinent to a grid but may be used elsewhere.
               items: items
             };
           /* for (var i=0 ; i < items.length; i++) { //this items loop will get the IDs, but will only pass the last value to our hyperlink field
            pid = items.ID;
            console.log(pid); 
            }*/
              //Create data store and bind to grid.
              relStore = new Memory({
              data: relData
              });
              //console.log(relStore);
              
              relGrid.set("store", relStore);
              //relGrid.set("query", {OBJECTID: "*"}); //filters what?
              
            });
            //if (!gridPane._showing) {
               //  gridPane.toggle();
            //}  
        }

     Not quite as elegant as yours but it works well and handles the links and sorting requierments needed for numeric columns in my dGrid.  I was asking about moving to featureTable because I am looking for maybe a little better dom node solution because I want the dGrid to play well on tablets without maybe having to go to dojox. 

Are you saying I should be able to pass this store into a featureTable instead of my dGrid?

0 Kudos
KellyHutchins
Esri Frequent Contributor

No you can't pass the store but you might be able to create a feature layer based on the results to provide to the table. Could be an easier solution - I can try to test it out early next week.

I'm a little confused about why you want to use FeatureTable instead of dgrid. Was there some particular capability in the FeatureTable that you wanted to take advantage of?  The FeatureTable is based on dojo's dgrid so if you are currently running into limitations with dgrid you may run into those same issues with the FeatureTable. 

0 Kudos
DavidColey
Frequent Contributor

Kelly, just to let you know I'm not really pursuing this.  I think I can get what I need via css on my dgrid.  I am experiencing a somewhat nagging issue where my rows are not lining up with the column headers at initial population.  If I then click a header, the rows/cells then align just fine.  If you have any css suggestion on this that would be great.

Thanks

David

0 Kudos
KellyHutchins
Esri Frequent Contributor

Sounds like a refresh issue. Are you using dojo border containers and content panes? If so maybe try and get the border container and refresh it after you populate the table.

DavidColey
Frequent Contributor

Hi Kelly, thanks I am:

<div id="bottomPane" data-dojo-type="dojox/layout/ExpandoPane" data-dojo-props="title:'', region:'bottom', startExpanded:false, splitter:'true'">
        <div id="bottomBorder" data-dojo-type="dijit/layout/BorderContainer" region="bottom" style="width: 100%;height:100%;">
                   <div id="cpBottom" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="splitter: 'false', region: 'center'">
                             <div id="divGrid"></div>
                   </div>
            </div>
  </div>

Grid Structure:

relGrid = new Grid({
          bufferRows : Infinity,
          loadingMessage: 'Loading data...',
          noDataMessage : "No Records Available",
              columns: 
               [
                {field: 'ID', label: 'Account', resizable: true},
                {field: 'LINK', label: 'Information', formatter: function(link) {return "<a target='_blank' href=" + link + ">Full In</a>";}, a   ble: true},
                {field: 'NAME1', label: 'Owner Name', formatter: toTitleCase, resizable: true},
                {field: 'JUST', label: 'Justified Value', formatter: toCurrency, resizable: true},
                {field: 'ASSD', label: 'Assessed Value', formatter: toCurrency, resizable: true},
                {field: 'YRBL', label: 'Year Built', resizable: true},
                {field: 'LSQFT', label: 'Recorded Area', formatter: convertPlaces, resizable: true}
                ] 
    }, "divGrid");
    console.log(relGrid);
    relGrid.startup();

To give you an idea of the flow, a the beggining of the ready function, I am setting up a girdPane var and wiring it to the bottomPane div:

gridPane = registry.byId('bottomPane');

and then toggling the gridPane open once the memory store is populated and passed its content to the grid:

if (!gridPane._showing) {
     gridPane.toggle();
     }

So I'm guessing I would call a refresh after my gridPane toggles?

Thanks

David

0 Kudos
KellyHutchins
Esri Frequent Contributor

Correct I'd try calling refresh after toggle.

DavidColey
Frequent Contributor

Thanks, I think I'm with you.  I'm somewhat doing that in another app where a user selects an attribute from a FilteringSelect, the results are passed to my dgrid while graphics are placed on the map.  Those graphics could become a featureLayer and that layer could then populate the FeatureTable, I think. 

To answer your question though, I wanted to explore the dijit's column adding functionality...

0 Kudos