Field Maps Arcade Expression - Why does calculated Arcade expressions only return read only variables?

1730
8
Jump to solution
09-14-2022 10:39 AM
by Anonymous User
Not applicable

Hello, I am new to field maps but have came across an issue that I am wondering if anyone else is having or would be a good feature request. I have two layers.. one is a master copy and one is an inspection layer. I am trying to pull information from the master file to populate my inspection (i.e. AssetID, AssetType). In some case, it is possible the AssetType is wrong, so I would like to be able to edit this feature. However the arcade expression only returns a "read only" field. My calculated expression is below by the way. If it does not paste correctly, I got it from this document. Its expression number 7 "Store information from a nearby feature" https://www.esri.com/arcgis-blog/products/field-maps/field-mobility/common-calculated-expressions-fo...

I know that I can just edit the master file to reflect this change and pull the correct information, but I think this could be verified in real time in the field if the returned AssetType was not a read-only attribute. Instead there is an extra step of having to manually edit the data. Wondering if anyone else has had this issue?

 

 


// If feature doesn't have geometry return null
if (IsEmpty(Geometry($feature))) { return null }

// Get the parcels layer
var parcels = FeatureSetByName($map, 'Parcels')

// Buffer the current location and intersect with parcels
var bufferedLocation = Buffer($feature, 100, 'feet')
var candidateParcels = Intersects(parcels, bufferedLocation)

// Calculate the distance between the parcel and the current location
// Store the feature and distance as a dictionary and push it into an array
var featuresWithDistances = []
for (var f in candidateParcels) {
Push(featuresWithDistances,
{
'distance': Distance($feature, f, 'feet'),
'feature': f
}
)
}

// Sort the candidate parcels by distance using a custom function
function sortByDistance(a, b) {
return a['distance'] - b['distance']
}
var sorted = Sort(featuresWithDistances, sortByDistance)

// Get the closest feature
var closestFeatureWithDistance = First(sorted)

// If there was no feature, return null
if (IsEmpty(closestFeatureWithDistance)) { return null }

// Return the address
return `${closestFeatureWithDistance['feature']['ADDNUM']}

0 Kudos
1 Solution

Accepted Solutions
JustinReynolds
Occasional Contributor III

The reason a field is read-only on a field that has a calculation is because of how and when the calculation is triggered.  Any change to any field will fire all calculations in the form (ALL of them).  As you complete your form all constraints and field calculations are fired for every little change that is made.  So if the field were editable, but also allowed to calculate, then it would just overwrite whatever you typed in once you moved to the next field or collapsed a group and so on.  Then you would fix it and it would again overwrite it in a vicious circle. 

There is also an order of operations when these events fire.  I'm not sure if they are officially documented anywhere, but based on my experience the following appears to happen:

  • In general, the form re-calcs all expressions (constraint and field calcs) in form order from top to bottom
  • Group constraints appear to fire first before any field expressions... not 100% on this one.
  • Field constraints expressions fire before a field calc so that it can check if the field calc even needs to occur

 

So calculated field being read-only is by design and is quite useful.  There are two ways that I handle a scenario where I need to calculate a value and allow the user to change that value. Both involve setting up an editability expression (see the constraint arcade profile) (Note: as of 09/2022 the ability to do this via the Fields Maps UI does not yet exist and thus must be done on the maps JSON via something like AGO Assistant or the ArcGIS Python API).

The first way is to tie the editability expression to a switch which then enables or disables the editability of my field that has a calculation based on the value of the switch (Yes/No, True/False, 1/0, etc.).  Toggling editability on the helper field has the effect of enabling or disabling the calculation on my field of interest which then allows the user to choose when to calculate or when to allow free reign.

The second way is to tie the editability expression to the calculated field itself.  In other words if my calculated value results in any value other than null or a particular value that you have in mind then my editability expression will return "True" and thus disable the calculation and allow me to edit the value of the field that was previously calculated.  

In both cases the editability expression is a property of the field that is being calculated.  The expression returns true or false (as all constraints must) based on any logic that uses any field(s) or geometry or data source you wish to use that happen to be accessible to the feature within the bounds of the constraint profile.

Below the service date is tied to a switch (case 1) and the service number field is tied to itself (case 2). Both are calculated fields and not editable by default.  For case 1, the switch toggling from yes or no determines a true or false return in my editability expression.  In case 2, my editability expression determines if the current field is null, if so it returns false, if not it returns true and along with it enables or disables the field calculation... unless I null the field again as I do below... then the process is retriggered..  This means both of the fields are dynamically calculated and editable and will never be submitted as null. I refer to these cases as dynamic default values when I need them to calc on insert but be editable there after.

 

Calculated Default Values ExampleCalculated Default Values Example

 

 

- Justin Reynolds, PE

View solution in original post

8 Replies
JustinReynolds
Occasional Contributor III

The reason a field is read-only on a field that has a calculation is because of how and when the calculation is triggered.  Any change to any field will fire all calculations in the form (ALL of them).  As you complete your form all constraints and field calculations are fired for every little change that is made.  So if the field were editable, but also allowed to calculate, then it would just overwrite whatever you typed in once you moved to the next field or collapsed a group and so on.  Then you would fix it and it would again overwrite it in a vicious circle. 

There is also an order of operations when these events fire.  I'm not sure if they are officially documented anywhere, but based on my experience the following appears to happen:

  • In general, the form re-calcs all expressions (constraint and field calcs) in form order from top to bottom
  • Group constraints appear to fire first before any field expressions... not 100% on this one.
  • Field constraints expressions fire before a field calc so that it can check if the field calc even needs to occur

 

So calculated field being read-only is by design and is quite useful.  There are two ways that I handle a scenario where I need to calculate a value and allow the user to change that value. Both involve setting up an editability expression (see the constraint arcade profile) (Note: as of 09/2022 the ability to do this via the Fields Maps UI does not yet exist and thus must be done on the maps JSON via something like AGO Assistant or the ArcGIS Python API).

The first way is to tie the editability expression to a switch which then enables or disables the editability of my field that has a calculation based on the value of the switch (Yes/No, True/False, 1/0, etc.).  Toggling editability on the helper field has the effect of enabling or disabling the calculation on my field of interest which then allows the user to choose when to calculate or when to allow free reign.

The second way is to tie the editability expression to the calculated field itself.  In other words if my calculated value results in any value other than null or a particular value that you have in mind then my editability expression will return "True" and thus disable the calculation and allow me to edit the value of the field that was previously calculated.  

In both cases the editability expression is a property of the field that is being calculated.  The expression returns true or false (as all constraints must) based on any logic that uses any field(s) or geometry or data source you wish to use that happen to be accessible to the feature within the bounds of the constraint profile.

Below the service date is tied to a switch (case 1) and the service number field is tied to itself (case 2). Both are calculated fields and not editable by default.  For case 1, the switch toggling from yes or no determines a true or false return in my editability expression.  In case 2, my editability expression determines if the current field is null, if so it returns false, if not it returns true and along with it enables or disables the field calculation... unless I null the field again as I do below... then the process is retriggered..  This means both of the fields are dynamically calculated and editable and will never be submitted as null. I refer to these cases as dynamic default values when I need them to calc on insert but be editable there after.

 

Calculated Default Values ExampleCalculated Default Values Example

 

 

- Justin Reynolds, PE
by Anonymous User
Not applicable

Hello, 

I like you solution. I can certainly see how either option (case 1 or 2) could be something I could implement. However, the implementation is something that I am struggling with. Do you have an example code. I was able to edit the JSON to change the editability of the field to True but I cant figure out how to implement the switch logic i.e. if valid value = do not edit and if null value = edit. I believe that is case 1 in your solution above? Does that happen in the JSON itself or in field Maps? Once again this may seem very basic so I appreciate your time and consideration.

0 Kudos
JustinReynolds
Occasional Contributor III

I posted an example JSON in a post on a similar topic.

https://community.esri.com/t5/arcgis-field-maps-ideas/calculated-default-values/idc-p/1177552#M721

Here is the key so that you don't have to write the the arcade expression in JSON yourself.  Basically I write an editableExpression in the field maps UI as if it were a visibilityExpression.  I don't assign this expression to anything in the Field Maps UI, rather once it is authored and saved I navigate to AGO Assistant and add the reference to that expression on my field on interest.

Step 1: Author the editable expression in Field Maps as if it were a visibility expression (don't assign it to anything). 

JustinReynolds_0-1663345563833.png

JustinReynolds_1-1663345599989.png

 

Step 2: Add the property to the field form element of interest in AGO Assistant.

>>> below "editable": false in the JSON add "editableExpression": "name of your expression here",

>>>>>> from the above screenshots for example ->

>>>>>>>>> "editableExpression": "constraint_edt_cond_service_date_not_null",

If you need a little more let me know and I'd be happy to help further.

If this helped you please consider marking my original comment as the solution.

Thanks

- Justin Reynolds, PE
by Anonymous User
Not applicable

Hello Justin , 

Thank you for your continued help. 

I am still having some issues implementing this. I have attached some screenshots for you 1)the conditional visibility arcade expression, 2) the name of the expression, 3) the JSON in AGO.

I believe I have implemented it the exact same as you. Note I tried both "expr52" and "Conditional_True_False" and neither worked. What am I missing?

Best, 

Anthony

0 Kudos
JustinReynolds
Occasional Contributor III

Hi Anthony

Which field is being calculated?  I would expect the editbleExpression to be on the field that has the calculation rather than the field that doesn't but I don't see a valueExpression in the test3 image above.  An in your case you would be using "expr52", but you may have assigned it to the wrong field.

- Justin Reynolds, PE
0 Kudos
by Anonymous User
Not applicable

Hello Justin, 

First thank you for the continued help. Please find attached bellow the JSON code I have. In addition, the editableExpression I created (which is a copy and paste from yours).. is not set to any field.

{
"label": "New Culvert Type",
"type": "field",
"editable": false,
"fieldName": "Culvert_New",
"inputType": {
"type": "combo-box",
"noValueOptionLabel": "No value",
"showNoValueOption": true
},
"editableExpression": "expr52",
"valueExpression": "expr/culvertid-expression"
},

test4.PNG

The field that is being calculated is "Culvert_New". The value expression i.e. expr/culvertid-expression.. grabs information from another layer regarding a culvert type. The editableExpression is the one I copied from your code in a previous reply.

 

Currently, When I select a culvert that has a culvert type information for (from my other layer).. this is what comes up. The Culvert type shows ... but it is editable (not the biggest deal that it is editable.. but it would be better if it wasn't).

IMG_8760.jpg

 

Then in a situation where I don't have Culvert Type information (from the other layer I am pulling from).. it comes up with a non-editable field. I was hoping here... this is where it would recognize the null and then make it editable so they can select the culvert type. I believe I have implemented it wrong because what you have proposed sounds like the exact solution I am looking for. 

IMG_8761.jpg

Once again this is also my Arcade expression

if(!IsEmpty($feature["Culvert_New"])){
console('Make Field Editable, prevent valueExpression execution');
return True;
} else {
console('Make field not editable, allow valueExpression execution')
return False
}

 

Once again I appreciate the continued help

0 Kudos
JustinReynolds
Occasional Contributor III

Hey Anthony,

I think I'm getting a handle on your situation.  Can you provide the arcade expression you have expr/culvertid-expression?  If you don't want to post it publicly you can DM me.  I'm thinking that we might be doing too much on this single field and that we just need a helper field to get this done, but I'd like to recreate the scenario to be sure.

Thanks

- Justin Reynolds, PE
0 Kudos
by Anonymous User
Not applicable

Hi Justin, 

I dont mind posting here just in case someone else ever has this same issue. Here is my other expression (i.e. culvertid-expression). I got this expression from here (Common Calculated Expressions for ArcGIS Field Maps (esri.com))

// If feature doesn't have geometry return null
if (IsEmpty(Geometry($feature))) { return null }

// Get the capital Structure layer
var capital = FeatureSetByName($map, 'GC_Culverts')

// Buffer the current location and intersect with parcels
var bufferedLocation = Buffer($feature, 328, 'f')
var candidateCapital = Intersects(capital, bufferedLocation)

// Calculate the distance between the parcel and the current location
// Store the feature and distance as a dictionary and push it into an array
var featuresWithDistances = []
for (var f in candidateCapital) {
Push(featuresWithDistances,
{
'distance': Distance($feature, f, 'feet'),
'feature': f
}
)
}

// Sort the candidate parcels by distance using a custom function
function sortByDistance(a, b) {
return a['distance'] - b['distance']
}
var sorted = Sort(featuresWithDistances, sortByDistance)

// Get the closest feature
var closestFeatureWithDistance = First(sorted)

// If there was no feature, return null
if (IsEmpty(closestFeatureWithDistance))
{return null}

// Return the address
return `${closestFeatureWithDistance['feature']['CulvertType']}`

Basically I have two layers. One is the master layer and one is the inspection layer. We dont want staff to be able to directly edit the master layer but we want to be able to pull information from the master layer and populate the inspection layer. This expression allows me to pull the Culvert information from the master to populate the inspection layer. In some cases there is valid culvert type information. The expected behavior if possible would be the editing would expression would ideal evaluate to false and not allow editing to that field... but in other situations culvert type is missing i.e. null. The expected behavior here would be the form would see the null value and evaluate the editing expression to true and allow editing. Something like that

Best, 

Anthony

0 Kudos