Select to view content in your preferred language

Removing empty fields from popup in Portal Map Viewer using Arcade Expression

2402
18
02-13-2024 01:56 AM
CJCowardin
Emerging Contributor

Trying to configure a pop-up to remove all of the fields with no data since the list is very long.  I tried using IsEmpty but they are still there.  Any help is appreciated. 

This is a sample popup

CJCowardin_0-1707818197175.png

 

0 Kudos
18 Replies
ZachBodenner
MVP Regular Contributor

I have a solution that I've repurposed quite a few times. Here's an Arcade expression where the return is an HTML table that will include only attributes that have a value present. You'll have to switch out the attributes to match your own. 

 

// Use Arcade > Text template
// create one expression for all fields
// return as table showing only populated fields
// If field is null, do not add row to table
// if field has a value, add row to table
 
// Create variables for checked-for-field and what their labels should be. 
// Select the fields to look for
var fieldsArray = ['Condition','Install_Date','Location','Intersection_Type','Facility_Type','Ramp_Config','Managed_By','Comments'];
// Set the field aliases in the same order as the fields above
var labelsArray = ['Condition','Install Date','Location','Intersection Type','Facility Type','Ramp Configuration','Managed By','Comments'];
var arrayLength = Count( fieldsArray );

// Table Header
// Identify condition and set background color accordingly
var cond = $feature.Condition
var backgroundColor = When(cond=="1 (Non-existant)","#fc461d",cond=="2 (Poor)","#df750c",cond=="3 (Functional)","#f2cc0d",cond=="4 (ADA Compliant)","#37aac4","#cccccc") 
// Set table header
var returnString = `<h3 style="background-color:${backgroundColor};padding-left:2%">Ramp Info</h3><table style="width:100%;background-color:#f4f4f4;">`;

// Make sure the final table is searching for correct attributes
Expects( $feature, 'Condition','Install_Date','Location','Intersection_Type','Facility_Type','Ramp_Config','Managed_By','Comments')

/*loops through a number of times eual to the length of the array (that's the i<arrayLength part), incrementing by one each time (i++). If the attribute if the field array is not empty 
(!IsEmpty), then the value of the the variable returnString is equal the value in the fieldsArray equal to the index value of the array (fieldsArray[i]) plus the additional portion of the
return string enclosed in the curly brackets. If it is empty, then the value of returnString   
*/

for (var i = 0; i < arrayLength; i++ ) {
  if ( !IsEmpty( $feature[ fieldsArray[i] ] ) ){
    //returnString = returnString + "<tr><td>" + labelsArray[i] + "</td><td>" + $feature[ fieldsArray[i] ] + "</td></tr>";
    returnString = `${returnString}<tr><td style="border-bottom: 1px solid #cccccc;border-right: 1px solid #cccccc;width:50%;padding-left:2%"><b>${labelsArray[i]}</b></td><td style="border-bottom: 1px solid #cccccc;width:50%;padding-left:2%">${$feature[ fieldsArray[i] ]}</td></tr>`;
  }}
 
returnString = returnString + "</table>";

return { 
   type : 'text', 
   text : returnString 
}

 

Here's what the result looks like (note that I also used one of the attributes, Condition, to determine the background color of the header:

 

ZachBodenner_0-1707833902624.png

 

Happy mapping,
- Zach
LAGIBOO
New Contributor

This was exactly what I have been looking for!  Couple questions I wonder if you can help me with?

 

1. In the header, instead of specifying the text like you have as "Ramp Info" can I have it pull an attribute field or two?  I would like to use $feature.Project_Title - $feature.Project_Status so it looks something like Generation - In Progress.

2. I noticed it is displaying a Date field as 2024-02-01T00:00:00-06:00 even though I have the field date format set to MM/DD/YYYY... is there any way I can get this to display just date and in the MM/DD/YYYY format (so 02/01/2024 in this case)?

 

Thank you!!

0 Kudos
ZachBodenner
MVP Regular Contributor

Yes to both! So my header variable is here:

`<h3 style="background-color:${backgroundColor};padding-left:2%">Ramp Info</h3><table style="width:100%;background-color:#f4f4f4;">`;

 

Just change the Ramp Info text to something that fits your needs

`<h3 style="background-color:${backgroundColor};padding-left:2%">${$feature.Project_Title} - ${$feature.Project_Status}</h3><table style="width:100%;background-color:#f4f4f4;">`;

 

As to dates, the display can be easily changed as well, but they would need to be pulled out of the loop return.

 

var date = Text($feature.datefield,'DD MMM, YYYY') //or whatever format
var dateReturn = `<tr><td style="border-bottom: 1px solid #cccccc;border-right: 1px solid #cccccc;width:50%;padding-left:2%"><b>Date: </b></td><td style="border-bottom: 1px solid #cccccc;width:50%;padding-left:2%">${date}</td></tr>`

for (var i = 0; i < arrayLength; i++ ) {
  if ( !IsEmpty( $feature[ fieldsArray[i] ] ) ){
    //returnString = returnString + "<tr><td>" + labelsArray[i] + "</td><td>" + $feature[ fieldsArray[i] ] + "</td></tr>";
    returnString = `${returnString}<tr><td style="border-bottom: 1px solid #cccccc;border-right: 1px solid #cccccc;width:50%;padding-left:2%"><b>${labelsArray[i]}</b></td><td style="border-bottom: 1px solid #cccccc;width:50%;padding-left:2%">${$feature[ fieldsArray[i] ]}</td></tr>`;
  }}

returnString = returnString + dateReturn + "</table>";

 

So what I did there was create a text variable for the date, plug it into a table row with two columns (same as the loop return table, and in the same style) and then place it after the loop, but before closing the table.

Happy mapping,
- Zach
0 Kudos
AdamGebhart
Frequent Contributor

@ZachBodenner 

Hi.  Do you know how to get the table result to be displayed horizontally to look more like this?

AdamGebhart_0-1743431487975.png

I'm trying to figure out how to do the horizontal display and remove the blank/null lines ('na' above) so that only populated records are returned.  My feature class has fields (index, book, page, imageURL) for documents one thru four.  Sometimes only doc1 is populated, sometimes it's only doc2 and so on.

0 Kudos
ZachBodenner
MVP Regular Contributor

So not knowing exactly how your data it set up, are you running this on a feature-to-table kind of relationship, so that each row in the table is represented by one index value? Give this a whirl:

// Format main table html
var tStyle = `<table style="width:100%;background-color:#f4f4f4;">`;
var trpt1 = `<tr><td style="color: #323232;border-bottom: 1px solid #cccccc;border-right: 1px solid #cccccc;width:50%;padding-left:2%"><b>`;
var trpt2 = `</b></td><td style="border-bottom: 1px solid #cccccc;border-right: 1px solid #cccccc;width:25%;padding-left:2%">`;
var trpt3 = `</b></td><td style="border-bottom: 1px solid #cccccc;border-right: 1px solid #cccccc;width:25%;padding-left:2%">`;
var trpt4 = `</td></tr>`;
var endTable = `</table>`;

// Format html for the column titles
var hrpt1 = `<tr><td style="color:#ffffff;background-color: #323232; border-bottom: 1px solid #cccccc;border-right: 1px solid #cccccc;width:50%;padding-left:2%"><b>`;
var hrpt2 = `</b></td><td style="color:#ffffff;background-color: #323232; border-bottom: 1px solid #cccccc;border-right: 1px solid #cccccc;width:25%;padding-left:2%">`;
var hrpt3 = `</b></td><td style="color:#ffffff;background-color: #323232; border-bottom: 1px solid #cccccc;border-right: 1px solid #cccccc;width:25%;padding-left:2%">`;
var hrpt4 = `</td></tr>`

var titleRow = `${hrpt1}Index${hrpt2}Book - Page${hrpt3}Document${hrpt4}`

// Get the GlobalID of the current tree feature
var Global = $feature.GlobalID;

// Retrieve related inspection records (change this to match the related table)
var relatedForms = FeatureSetByName($map, "Tree Inventory Form");

// Define a filter statement to get only related records for this tree
var filterStatement = "TreeInv_Rel = @Global";

// Apply the filter to get only the relevant inspection forms
var inspForm = Filter(relatedForms, filterStatement);

// Begin final return string by setting the header and opening the table
// Also, add the column titles
var returnString = `<h3 style="color:#ffffff;background-color:#ea0c12;padding-left:2%">Right of Way Documents</h3> ${tStyle} ${titleRow}`;

// Loop through the related records and add rows
for (var f in inspForm) {

    var i = f.ObjectID;
    var b = f.Condition;
    var p = f.Performed_By;
    var d = f.Maintenance_Performed;
    
    if (!IsEmpty(i)) {
        returnString += `${trpt1}${i}${trpt2}${b} - ${p}${trpt3}${d}${trpt4}`;
    }
}


// Close the table
returnString += endTable;

// Return the formatted string as a text output
return { 
    type: 'text', 
    text: `${returnString}`
}

 

I used my tree inventory as an example, and the Object ID as the check to see if the row was empty or not, so obviously you would need to change that variable 'i' to "var i = f.Index". But this is what I get:

ZachBodenner_0-1743436355240.png

Also, the field I used in the 'Document' column isn't a hyperlink, so you'll probably have to format that as  <h ref> but this should get you most of the way there!

 

Happy mapping,
- Zach
AdamGebhart
Frequent Contributor

Thanks @ZachBodenner .  I'll try out your element with my stuff this afternoon.

In case this helps answer your questions, I have a map service referencing a polygon feature class that shows source document locations for right-of-way easements, acquisitions, etc.  We have up to four possible documents, and like I said in the previous post, not all shapes have all four document fields (book type, book #, page #, imageURL) populated.  In fact, few do out of 2,000+ features.

Below is what my html table looks like in the Text element.  Essentially, book1 doc fields are the first line and the fields are referenced via expressions 0 thru 3.  Book2 doc fields are line 2 and expressions 4 thru 7.  I'll always be pointing to an expression (based on field values) to populate the table rows instead of directly displaying the values from the feature class.

 

AdamGebhart_0-1743440953377.png

 

0 Kudos
ZachBodenner
MVP Regular Contributor

Ah okay so your data is like, index1, book1, page1, doc1, index2, book2, page2, doc2, etc? You have a finite number of possible documents?

If that's the case your situation gets a little simpler because you don't actually need to loop through anything. 

Essentially you could do this instead: 

// Format main table html
var tStyle = `<table style="width:100%;background-color:#f4f4f4;">`;
var trpt1 = `<tr><td style="color: #323232;border-bottom: 1px solid #cccccc;border-right: 1px solid #cccccc;width:50%;padding-left:2%"><b>`;
var trpt2 = `</b></td><td style="border-bottom: 1px solid #cccccc;border-right: 1px solid #cccccc;width:25%;padding-left:2%">`;
var trpt3 = `</b></td><td style="border-bottom: 1px solid #cccccc;border-right: 1px solid #cccccc;width:25%;padding-left:2%">`;
var trpt4 = `</td></tr>`;
var endTable = `</table>`;

// Format html for the column titles
var hrpt1 = `<tr><td style="color:#ffffff;background-color: #323232; border-bottom: 1px solid #cccccc;border-right: 1px solid #cccccc;width:50%;padding-left:2%"><b>`;
var hrpt2 = `</b></td><td style="color:#ffffff;background-color: #323232; border-bottom: 1px solid #cccccc;border-right: 1px solid #cccccc;width:25%;padding-left:2%">`;
var hrpt3 = `</b></td><td style="color:#ffffff;background-color: #323232; border-bottom: 1px solid #cccccc;border-right: 1px solid #cccccc;width:25%;padding-left:2%">`;
var hrpt4 = `</td></tr>`

// Set the different style for the title row.
var titleRow = `${hrpt1}Index${hrpt2}Book - Page${hrpt3}Document${hrpt4}`

var i1 = $feature.index1
var b1= $feature.book1
var p1 = $feature.page1
var d1 = $feature.indexURL1

var i2 = $feature.index2
var b2= $feature.book2
var p2 = $feature.page2
var d2 = $feature.indexURL2

var i3 = $feature.index3
var b3= $feature.book3
var p3 = $feature.page3
var d3 = $feature.indexURL3

var i4 = $feature.index4
var b4= $feature.book4
var p4 = $feature.page4
var d4 = $feature.indexURL4

// If the index is not empty, return a table row. Otherwise, leave it empty
var row1 = IIF( !IsEmpty( i1),   `${trpt1}${i1}${trpt2}${b1} - ${p1}${trpt3}${d1}${trpt4}`,'')
var row2 = IIF( !IsEmpty( i2),  `${trpt1}${i2}${trpt2}${b2} - ${p2}${trpt3}${d2}${trpt4}`,'')
var row3 = IIF( !IsEmpty( i3),   `${trpt1}${i3}${trpt2}${b3} - ${p3}${trpt3}${d3}${trpt4}`,'')
var row4 = IIF( !IsEmpty( i4),   `${trpt1}${i4}${trpt2}${b4} - ${p4}${trpt3}${d4}${trpt4}`,'')


// BSetup final variable, which includes header, title row, and each row's conditional visibility
var returnString = `<h3 style="color:#ffffff;background-color:#ea0c12;padding-left:2%">Right of Way Documents</h3> 
  ${tStyle} 
    ${titleRow}
    ${row1}
    ${row2}
    ${row3}
    ${row4}
  ${endTable}`


// Return the formatted string as a text output
return { 
    type: 'text', 
    text: `${returnString}`
}

 

If this is the case, you can basically set up your table manually since you know the maximum number of rows possible.

Happy mapping,
- Zach
AdamGebhart
Frequent Contributor

@ZachBodenner 

Thanks again.  I'm moving in the right direction.  Two more things though.

1) In place of the field names I need to reference expressions that I've created to better display the information.  I haven't been able to get the syntax right - do you know how to do that?  For instance, in the doc1 variables I need...

i1 = expression/expr0 (instead of $feature.index1)

b1 = expression/expr1 (instead of $feature.book1)

Etc.  I've tried $expression/expr0, ${expression/exp0}, {expression/exp0} and the code won't run.  I can add these expressions as variables in the element but that will add A LOT of code that I don't want in it if I don't need it there.

2) Any idea how to remove the white space between the header (red) and title row (dark gray) so that it looks like the image in first my post from today?

AdamGebhart_0-1743449770970.png AdamGebhart_1-1743450274454.png

 

 

0 Kudos
ZachBodenner
MVP Regular Contributor

As to point 2, you could probably just change the HTML away from an H3 tag and just create a new table row with only one column (<td>)

As to the first, Arcade Elements in the popup can't reference the expressions you've already written, at least as far as I know. You would have to include those in the Arcade elements itself.

Happy mapping,
- Zach