Popup content w/ Relationship Query

2031
18
Jump to solution
02-26-2019 11:20 AM
MKF62
by
Occasional Contributor III

I'm going to ask a pretty much straight JS question because stackexchange can't wrap their minds around the fact that there are javascript platforms that they've never seen before (arcigs javascript api basically).... so, sorry this is going to be a bit out of place but hopefully someone here will understand what I'm saying better.

I am trying to populate a InfoTemplate object with content using related table records of a polygon layer. I run the relationship query sucessfully, and then I need to loop through the records and put them in an HTML table format so I can display it in the popup. The problem I'm having is that I'm not sure how to write the code so the asynchronous relationship query finishes before the popup content gets rendered. The anonymous callback function won't finish before the return statement is hit in the function, thus my popup renders the table headers but nothing else. I'm sure I could just shift around the code using a feature layer on click event and do it that way, but I feel that I'm so close to having it done this way that maybe there's a simpler fix.

I've fiddled around with JS Promises and moving the anonymous callback function to it's own named function, but just can't seem to figure out how to make those work together.

//Call the function that will format the popup content
mgmtTractPopupBox.setContent(mgmtPopupContent);

function mgmtPopupContent(feature) {
    //set up the query properties
    //....
    //Create table header that will go inside popup
    var content = '<table id="mgmtPopupTable1"><tr><th>Veg Mgmt Practice</th><th>Herbicide</th><th>Month</th><th>Year</th>\
            <th>Implemented By</th><th>Funded By</th><th>Farm Bill Code</th></tr>';
    //Run the query - asynchronous process
    queryableMgmtTractFL.queryRelatedFeatures(relatedQuery, function (relatedRecords) {
        var fset = relatedRecords[OID].features;
        fset.forEach(function (feature) {
            var vegPractice = vegPName(feature.attributes.VegMgmtPractice);
            var herbicide = herbName(feature.attributes.Herbicide);
            var monthTreated = monthName(feature.attributes.MonthTreated);
            var yearTreated = feature.attributes.YearTreated;
            var impBy = impName(feature.attributes.ImplementedBy);
            var fundBy = fundName(feature.attributes.FundedBy);
            var fbc = feature.attributes.FarmBillCode;
            if (fundBy == "CRP" || fundBy == "CRP - CREP") {
                fbc = crpName(fbc);
            }
            else if (fundBy == "EQIP" || fundBy == "EQIP - RCPP") {
                fbc = eqipName(fbc);
            }
            else {
                fbc = "Not applicable";
            }
            row = '<tr><td>' + vegPractice + '</td><td>' + herbicide + '</td><td>' + monthTreated + '</td><td>' + yearTreated +
                '</td><td>' + impBy + '</td><td>' + fundBy + '</td><td>' + fbc + '</td></tr>';
            content = content + row;
        });
        content = content + '</table>';
    });
    return content; //Returns table header with none of the derived rows 
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
18 Replies
RobertScheitlin__GISP
MVP Emeritus

Molly,

  So you have two different FeatureLayers for the same layer

queryableMgmtTractFL and hbMgmtTractFL?
0 Kudos
MKF62
by
Occasional Contributor III

Yes. hbMgmtTractFL is a dynamic layer that displays on the map. queryableMgmtTractFL is a feature layer that is not displayed but is the same layer hbMmgtTractFL. hbMgmtTractFL is not queryable in that there is a join on it which apparently queries don't play nice with, so I've had to create the second feature layer. The user will be clicking features in hbMgmtTractFL, the popup content is generated using queryableMgmtTractFL.

Regardless, the hbMgmtTractFL is defined. 

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Molly,

  OK that is the problem then a dynamic map service layer is just an image and does not have a click event for features like a FeatureLayer does.

0 Kudos
MKF62
by
Occasional Contributor III

Well that is a unfortunate.. guess it's back to drawing board on how to set a definition expression on my feature layer that's derived from joined data (which I don't think there is another way). That leaves me with figuring out to query joined data which I also don't think is possible.

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Molly,

  Do you have to have the layer as a ArcGISDynamicMapServiceLayer?

0 Kudos
MKF62
by
Occasional Contributor III

I do. I have to join the data in order to display it correctly on my map. I have a polygon layer that has a 1-M relationship with records in a table. The records in the table have a "Year" field where the year could be different, so one polygon may have multiple records that have different values in the "Year" field. There is no "Year" field in the polygon layer since some polygons may need to have multiple years associated with it. The problem is I need to be able to let the user set a year in the map and show the polygons that has at least one record with that year, so I need to set a definition expression on the layer based on the year. Since the polygon layer itself does not have the years in it, I need to join the data to get that year to set the definition expression.

I may have figured out a way around it though. The QueryTask allows you to use a dynamicLayer, so if I query that, I believe the joined data will come with it and I can display it that way. We'll see if that works...

var mgmtQueryTask = new QueryTask('https://xxx/rest/services/HabitatMonitoring/HabitatData/MapServer/dynamicLayer', {
    source: lyrDataSource
}    ‍‍‍‍‍‍
MKF62
by
Occasional Contributor III

Nope... QueryTask doesn't work. What's the point of saying you can query a dynamicLayer when you can't query it when it has a join? Either the documentation is wrong or it's a bug.

mgmtTractPopupBox.setContent(mgmtPopupContent);

function mgmtPopupContent(feature) {
    for (var attrb in feature.attributes) {
        if (attrb == "HabitatManagement.DBO.MgmtTracts.OBJECTID") {
            var OID = feature.attributes[attrb];
        }
    }
    var mgmtQuery = new Query();
    mgmtQuery.objectIds = [OID];
    mgmtQuery.outFields = '
  • '
  • ; var mgmtQueryTask = new QueryTask('https://xxx/rest/services/HabitatMonitoring/HabitatData/MapServer/dynamicLayer', { source: lyrDataSource }); mgmtQueryTask.execute(mgmtQuery, mgmtQueryResults); //Format content for popup function mgmtQueryResults (featureSet) { console.log(featureSet); } }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

    Error I get in the console (It's an arc JS error, not the JS I wrote):

    TypeError: g.join is not a function
    at Object.toJson (init.js:1284)
    at Object.execute (init.js:2223)
    at Object.c.(anonymous function) [as execute] (https://js.arcgis.com/3.24/init.js:1214:326)
    at Object.mgmtPopupContent [as content] (HabitatMap.js?v=1:539)
    at c.getContent (init.js:1244)
    at Object._updateWindow (init.js:1191)
    at Object._featureSelected (init.js:1191)
    at Object.<anonymous> (init.js:63)
    at Object.c [as onSelectionChange] (init.js:119)
    at Object.select (init.js:1229) "TypeError: g.join is not a function
    at Object.toJson (https://js.arcgis.com/3.24/init.js:1284:378)
    at Object.execute (https://js.arcgis.com/3.24/init.js:2223:30)
    at Object.c.(anonymous function) [as execute] (https://js.arcgis.com/3.24/init.js:1214:326)
    at Object.mgmtPopupContent [as content] (http://localhost:14902/Scripts/HabitatMap.js?v=1:539:31)
    at c.getContent (https://js.arcgis.com/3.24/init.js:1244:248)
    at Object._updateWindow (https://js.arcgis.com/3.24/init.js:1191:450)
    at Object._featureSelected (https://js.arcgis.com/3.24/init.js:1191:351)
    at Object.<anonymous> (https://js.arcgis.com/3.24/init.js:63:337)
    at Object.c [as onSelectionChange] (https://js.arcgis.com/3.24/init.js:119:193)
    at Object.select (https://js.arcgis.com/3.24/init.js:1229:278)"

    0 Kudos
    MKF62
    by
    Occasional Contributor III

    Alright, so I finally figured out a way around the dynamic layer and using the polygon feature layer instead. My final issue is setting the definition expression on the polygon layer. I have the expression written out that matches the GUIDs I have in the polygon layer. Unfortunately, after I set the definition expression, it doesn't seem to apply because none of my polygons show up in the map. 

    //Set definition expression of hbMgmtTractFL before adding to map so it displays correctly
    //Get OIDs of all current year records in table (defintion expression is set on table)
    hbMgmtTableFL.on("load", function () {
        var recordIDs = new Query();
        recordIDs.where = "1=1";
        hbMgmtTableFL.queryIds(query, function (OIDs) {
            //Create relationship query
            var relatedRecordsQuery = new RelationshipQuery();
            relatedRecordsQuery.objectIds = OIDs;
            relatedRecordsQuery.outFields = ["MgmtTractID"];
            relatedRecordsQuery.relationshipId = 0;
            hbMgmtTableFL.queryRelatedFeatures(relatedRecordsQuery, function (fset) {
                //Get all MgmtTractIDs for the current year
                var MgmtTractIDsArr = [];
                for (i = 0; i < OIDs.length; i++) {
                    var OID = OIDs[i];
                    var feature = fset[OID].features;
                    var mgmtTractId = feature[0].attributes.MgmtTractID;
                    if (!(MgmtTractIDsArr.includes(mgmtTractId))){
                        MgmtTractIDsArr.push(mgmtTractId);
                    };
                }
                //Create the definition expression
                var whereClause;
                for (i = 0; i < MgmtTractIDsArr.length; i++) {
                    if (i == 0) {
                        whereClause = "MgmtTractID = '" + MgmtTractIDsArr[i] + "'";
                    }
                    else {
                        whereClause = whereClause + " || MgmtTractID = '" + MgmtTractIDsArr[i] + "'";
                    }
                }
                //Set the polygon feature layer's definition expression based on the GUIDs
                console.log(whereClause);
                hbMgmtTractFL.setDefinitionExpression(whereClause);
                hbMgmtTractFL.refresh();
            });
        });
    })

    Instead of on load of the table feature layer, I've tried doing it on the map after all the layers have been drawn, but no dice. I've tried calling the refresh function on the polygon feature layer after I have set the definition expression as well. I can't return the whereClause variable because it's inside a queryRelatedFeatures method so what would be returned is a deferred object, granted I don't think that getting the whereClause out of the functions would make a difference. There are no errors in the console and I've verified the whereClause variable comes through correctly. I'm stumped.

    The ultimate goal is that when the map initializes the layer will display with the definition expression set without the user having to click any buttons beforehand.

    0 Kudos
    MKF62
    by
    Occasional Contributor III

    Figured it out... dumb SQL mistake, using the javascript operator of "or" instead of the SQL operator of "or" in my definition expression. Final code that's FINALLY working. With the records that come out of the initial query task (on hbMgmtTableFL), I should be able to set the popup with those values for each polygon that is visible on the map. I'll have to format the popup on click of a feature with another ".queryRelatedFeatures(...)" call.

    //After adding the layers to the map get the initial definition expression of the tract feature layer
    map.on("layers-add-result", function () {
        getDefinitionExpression();
    });
    
    function getDefinitionExpression() {
        var recordIDs = new Query();
        recordIDs.where = "1=1";
        hbMgmtTableFL.queryIds(query, function (OIDs) {
            //Create relationship query
            var relatedRecordsQuery = new RelationshipQuery();
            relatedRecordsQuery.objectIds = OIDs;
            relatedRecordsQuery.outFields = ["MgmtTractID"];
            relatedRecordsQuery.relationshipId = 0;
            hbMgmtTableFL.queryRelatedFeatures(relatedRecordsQuery, function (fset) {
                //Get all MgmtTractIDs for the current year
                var MgmtTractIDsArr = [];
                for (i = 0; i < OIDs.length; i++) {
                    var OID = OIDs[i];
                    var feature = fset[OID].features;
                    var mgmtTractId = feature[0].attributes.MgmtTractID;
                    if (!(MgmtTractIDsArr.includes(mgmtTractId))) {
                        MgmtTractIDsArr.push(mgmtTractId);
                    };
                }
                //Create the definition expression
                var whereClause;
                for (i = 0; i < MgmtTractIDsArr.length; i++) {
                    if (i == 0) {
                        whereClause = "MgmtTractID = '" + MgmtTractIDsArr[i] + "'";
                    }
                    else {
                        whereClause = whereClause + " OR MgmtTractID = '" + MgmtTractIDsArr[i] + "'";
                    }
                }
                //Set the polygon feature layer's definition expression based on the GUIDs
                hbMgmtTractFL.setDefinitionExpression(whereClause);
                hbMgmtTractFL.refresh();
            });
        });
    };‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
    0 Kudos