Arcade expressions for popup all in one place or file.

2010
7
01-13-2021 08:06 AM
Status: Open
TanGnar
Occasional Contributor

I think it would be useful to define all of your arcade expressions in one file and call those expressions in the popup where needed, instead of having to create expressions in the 'Attribute Expressions' box individually. Something like this pseudo code:

 

 

exp doThisThing {
    some arcade expression
}

exp doAnotherThing {
    some arcade expression
}

 

 

Then when I'm building out my popup, I could just reference the defined expressions in the custom popup configuration. 

The benefit of this approach would be that I could edit expressions all in one place. It can be cumbersome to make changes to multiple expressions when they are complex and you need to make changes that apply to all. 

Bonus if you could define some things globally that could be used in multiple expressions so you don't have to do it for each individual expression. 

Edited to note that @PeterKnoop's explanation below is a good addition. 

7 Comments
PeterKnoop

Sometimes there is overlap in your Arcade Expressions, and you want and copy-and-paste a section from one to another. That is a cumbersome process currently, as you have to navigate out of the expression you are looking at, go to the other expression to copy the content you need, and then navigate back to the original expression and paste in the content.  If all the expressions were in one place, that would make this kind of process a lot easier.

Also, if there was some notion of global, as mentioned, then I might not need to copy-and-paste between multiple Arcade Expressions. Instead I could put the common code in a function, which multiple expressions could reference.

by Anonymous User

Thanks for the idea and comments on this - how close does the Existing tab in the Arcade editor, used to see and copy/paste existing expressions in the same profile, get to this ask? It's not the same as defining functions that can be reused, since you would need to copy in the function definition to each expression, but I believe it might help. 

ArcadeProfile.gif

TanGnar

@Anonymous User I have to admit I either overlooked that 'Existing' tab, or it is very new. 

Anyways, that is definitely helpful for reusing pieces of code across multiple expressions, thanks!

Maybe the Existing tab idea could be expanded. What if all of your expressions were managed in an Expression Manager that looked a lot like that Existing tab. All of your expressions are listed there, and you could easily move between them to make edits - click an expression name on the right, edit the code on the left and save. There could be find and replace, which could be set to within the expression or for all of your expressions.

The idea of global scope (sorry if that term is not used correctly). What if you can set an expression to be global, which means it can be referenced by other expressions. When meat of multiple expressions may all be the same, you could define that chunk once and reference that global expression where needed. 

Or expressions could possibly accept parameters like functions. Could be referenced (in the popup at least) like {expression/expr2, field1, field4}. 

PeterKnoop

@Anonymous User I too have entirely missed the "Existing" tab in the past! It would certainly save time in some cases for cutting-and-pasting, though its scope seems to be limited to an individual layer?

The way you've interpreted "global scope" is what I was thinking of: common functions that can be referenced by multiple arcade expressions. 

Though when I am editing a web map, I am thinking of "global" as being at the level of the web map, so including all the layers. It looks like the scope of the Existing tab may be limited to just an individual layer in a web map?

So while Existing enables me to access symbology arcade expressions for a layer when I'm configuring a popup arcade expression for the same layer, it looks like I cannot see arcade expressions from other layers in the web map I'm editing?  (If I was editing the visualization settings for a layer under its Item Details, then I would expect "global" in that context to be just the layer.)

Passing parameters to functions sounds intriguing, though I cannot think of a user story that would call for it off the top of my head. It feels, however, like that capability would be important if global functions were supported at the level of the web map.

 

 

by Anonymous User

Thanks for the elaboration and responses here! Re-using expressions is currently limited to an individual profile (e.g. pop-ups, style, labels) within a layer. I think a big reason for this is that Global variables such as $feature.<any attribute> are likely to be pretty different layer to layer, and different functions are supported in different profiles (e.g. no FeatureSets in style or labeling profile). 

TanGnar

I can see that it wouldn't make quite as much sense to define things outside of one layer. Unless some custom functions could be defined and accept parameters and then could be re-used.

An simple example use case for passing parameters to an expression:

This example really still applies to a single layer, but could possibly be extrapolated beyond. 

Say you have a bunch of numeric data and some kind of total column, illustrated in table below. You want to report for each row the Value as a ratio of total and of each other. You could have one expression for a ratio, which would take two arguments; could be fields, or could be some set value. 

IDValue1Value2Value3Value4TOTAL
A1266024
B10100525
C010101030

 

Then in the popup configuration, you just call that one expression where you want it along with the fields you want to use in the expression:

POPUP

Station A

Value 1 / TOTAL: {expression/expr0, Value1, TOTAL}
Value 2 / TOTAL: {expression/expr0, Value2, TOTAL}
Value 3 / TOTAL: {expression/expr0, Value3, TOTAL}

Value 1 / Value 2: {expression/expr0, Value1, Value2} 
etc...

I know you could achieve this example in one expression and return the lines with text formatting. But you loose some control of formatting that way. Or some If or When statements in a single expression to account for different fields. But things get a little messier if it were a more complex expression.

Possibly I'm missing something existing that makes this easier than I'm thinking about it here. If so, this discussion may not be helpful :). 

AdminAccount2

@TanGnar @Anonymous User @PeterKnoop 

I have a perfect use case:

I have an inspection in S123, where the responses are Yes or No. I'm trying to code the popup so that the "failed" results of the inspection stand out from passed checks. The problem is that "No" responses don't always mean the check fails. I built functionality into the survey to return a 1 or a 0, where 1 is pass and 0 is fail. 

SO

It would be slick if I could built one expression like this:

 

function issue_detected(response, isIssue){
    IF (isIssue == "0"){
        return "Check Failed"
    }
    ELSE IF (isIssue == "1"){
        return "Check Passed"
    }
    ELSE{
        return "something went wrong"
    }
}

 

AND 

could pass arguments to the function in-line, rather than hard coding each variable into the Arcade expression. As @TanGnar mentioned:

{expression/expr0, $feature.check1, $feature.check1_pass_fail}
{expression/expr0, $feature.check2, $feature.check2_pass_fail}
{expression/expr0, $feature.check3, $feature.check3_pass_fail}

Rather than maintaining three (or in my case, ~50) separate, pseudo-carbon-copied expressions...

//{expression/expr0}
function issue_detected(response, isIssue){
    IF (isIssue == "0"){
        return "Check Failed"
    }
    ELSE IF (isIssue == "1"){
        return "Check Passed"
    }
    ELSE{
        return "something went wrong"
    }
}

issue_detected($feature.check1, $feature.check1_pass_fail)

//{expression/expr1}
function issue_detected(response, isIssue){
    IF (isIssue == "0"){
        return "Check Failed"
    }
    ELSE IF (isIssue == "1"){
        return "Check Passed"
    }
    ELSE{
        return "something went wrong"
    }
}

issue_detected($feature.check2, $feature.check2_pass_fail)

//{expression/expr2}
function issue_detected(response, isIssue){
    IF (isIssue == "0"){
        return "Check Failed"
    }
    ELSE IF (isIssue == "1"){
        return "Check Passed"
    }
    ELSE{
        return "something went wrong"
    }
}

issue_detected($feature.check3, $feature.check3_pass_fail)