<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Editing with calculated field expressions | Sample | ArcGIS Maps SDK for JavaScript</title>
<link rel="stylesheet" href="https://js.arcgis.com/5.0/esri/themes/light/main.css" />
<!-- Load the ArcGIS Maps SDK for JavaScript from CDN -->
<script type="module" src="https://js.arcgis.com/5.0/"></script>
<script type="text/plain" id="tree-dbh">
// Tree info group -> Diameter at breast height(DBH) field
var circumference = $feature.circumference;
if (IsEmpty(circumference)) {
return Round((circumference / PI),1)
<script type="text/plain" id="nearest-address">
// Location group -> Nearest address field
if (isEmpty(Geometry($feature))) {
// Buffer distance (in feet)
var bufferDistance = 300;
var bufferGeom = Buffer($feature, bufferDistance, "feet")
// query for nearby address points
var addresses = Intersects(FeatureSetByName($map, "Address points",["FULL_ADDRESS"],true), bufferGeom);
if (Count(addresses) == 0) {
var minDistance = Infinity;
// iterate through the address points
for (var address in addresses) {
var addressDistance = Distance(address, $feature, "feet");
if (addressDistance < minDistance) {
nearestAddress = address;
minDistance = addressDistance;
// return street address from nearest
return Proper(nearestAddress.FULL_ADDRESS);
<script type="text/plain" id="municipality">
// Location group -> Municipality field
if (IsEmpty(geometry($feature))) {
return 'City of Redlands';
<script type="text/plain" id="district">
if (IsEmpty(Geometry($feature))) {
var districts = Intersects( FeatureSetByName($map,"Districts"), $feature);
if (IsEmpty(districts)) {
var district = First(districts);
return district.DISTRICT;
<script type="text/plain" id="latitude">
// Location group -> Latitude field
function MetersToLatLon(x, y) {
// Converts XY point from Spherical Mercator EPSG:900913 to lat/lon in WGS84 Datum
// Source: http://www.maptiler.org/google-maps-coordinates-tile-bounds-projection/
var originShift = 2.0 * PI * 6378137.0 / 2.0;
var lon = (x / originShift) * 180.0;
var lat = (y / originShift) * 180.0;
lat = 180.0 / PI * (2.0 * Atan( Exp( lat * PI / 180.0)) - PI / 2.0);
if (IsEmpty(Geometry($feature))) {
// use function to get the desired output
var latlon = MetersToLatLon(Geometry($feature).X, Geometry($feature).Y);
return Round(latlon[0],6);
<script type="text/plain" id="longitude">
// Location group -> Longitude field
function MetersToLatLon(x, y) {
// Converts XY point from Spherical Mercator EPSG:900913 to lat/lon in WGS84 Datum
// Source: http://www.maptiler.org/google-maps-coordinates-tile-bounds-projection/
var originShift = 2.0 * PI * 6378137.0 / 2.0;
var lon = (x / originShift) * 180.0;
var lat = (y / originShift) * 180.0;
lat = 180.0 / PI * (2.0 * Atan( Exp( lat * PI / 180.0)) - PI / 2.0);
if (IsEmpty(Geometry($feature))) {
// use function to get the desired output
var latlon = MetersToLatLon(Geometry($feature).X, Geometry($feature).Y);
return Round(latlon[1],6);
<script type="text/plain" id="current-user">
// Ownership group -> Collected By field
var portalURL = "https://www.arcgis.com";
var userInfo = GetUser(Portal(portalURL),"");
return (userInfo["fullname"])
] = await $arcgis.import([
"@arcgis/core/WebMap.js",
"@arcgis/core/views/MapView.js",
"@arcgis/core/layers/FeatureLayer.js",
"@arcgis/core/widgets/Editor.js",
"@arcgis/core/form/ExpressionInfo.js",
"@arcgis/core/form/FormTemplate.js",
"@arcgis/core/form/elements/GroupElement.js",
"@arcgis/core/form/elements/FieldElement.js",
//================================================
// Set the expressionInfos for the tree formTemplate
//================================================
const trueExpression = new ExpressionInfo({
const falseVisExpression = new ExpressionInfo({
name: "no-editing-allowed",
const dbhExpression = new ExpressionInfo({
expression: document.getElementById("tree-dbh").text,
const nearAddressExpr = new ExpressionInfo({
expression: document.getElementById("nearest-address").text,
title: "Nearest Address",
const muniExpression = new ExpressionInfo({
expression: document.getElementById("municipality").text,
const districtExpression = new ExpressionInfo({
expression: document.getElementById("district").text,
const latExpression = new ExpressionInfo({
expression: document.getElementById("latitude").text,
const longExpression = new ExpressionInfo({
expression: document.getElementById("longitude").text,
const currentUserExpression = new ExpressionInfo({
expression: document.getElementById("current-user").text,
//================================================
// Set of field elements for grouping tree species
//================================================
// Common tree name field element
const commonTreeFieldElement = new FieldElement({
// autocastable to ComboBoxInput
// Genus name field element
const genusFieldElement = new FieldElement({
// autocastable to ComboBoxInput
showNoValueOption: false,
// Tree species field element
const treeSpeciesFieldElement = new FieldElement({
// autocastable to ComboBoxInput
showNoValueOption: false,
// First grouping of elements for tree species
const groupElementTreeSpecies = new GroupElement({
elements: [commonTreeFieldElement, genusFieldElement, treeSpeciesFieldElement],
initialState: "collapsed",
//======================================
// Field elements for Tree info grouping
//======================================
// Tree circumference field element
const circumFieldElement = new FieldElement({
fieldName: "circumference",
//autocastable to TextBoxInput
// Diameter at breast height field element.
// Since this field element uses a value expression, the editableExpression property must evaluate to false
const dbhFieldElement = new FieldElement({
description: "Computed from Tree Circumference, (in inches)",
label: "Diameter at breast height (DBH)",
editableExpression: falseVisExpression.name,
// autocastable to TextBoxInput
valueExpression: dbhExpression.name,
// Second grouping of elements for Tree info
const groupElementTreeInfo = new GroupElement({
elements: [circumFieldElement, dbhFieldElement],
initialState: "collapsed",
//======================================
// Field elements for Location grouping
//======================================
// Nearest address field element
const addressFieldElement = new FieldElement({
label: "Nearest address",
editableExpression: falseVisExpression.name,
// autocastable to TextBoxInput
valueExpression: nearAddressExpr.name,
// Municipality field element
const muniFieldElement = new FieldElement({
editableExpression: falseVisExpression.name,
fieldName: "municipality",
// autocastable to TextBoxInput
valueExpression: muniExpression.name,
// District field element
const distFieldElement = new FieldElement({
label: "District or neighborhood",
editableExpression: falseVisExpression.name,
// autocastable to TextBoxInput
valueExpression: districtExpression.name,
// Latitude field element
const latFieldElement = new FieldElement({
editableExpression: falseVisExpression.name,
valueExpression: latExpression.name,
// Longitude field element
const longFieldElement = new FieldElement({
editableExpression: falseVisExpression.name,
valueExpression: longExpression.name,
// Third grouping of elements for location
const groupElementLocation = new GroupElement({
initialState: "collapsed",
//======================================
// Field elements for Ownership grouping
//======================================
// Collected by field element
const collNameFieldElement = new FieldElement({
editableExpression: falseVisExpression.name,
fieldName: "collector_name",
// autocastable to TextBoxInput
valueExpression: currentUserExpression.name,
// Owner name field element
const ownedByFieldElement = new FieldElement({
editableExpression: trueExpression.name,
// autocastable to RadioButtonsInput
noValueOptionLabel: "No value",
// Maintainer name field element
const maintainFieldElement = new FieldElement({
// autocastable to RadioButtonsInput
showNoValueOption: false,
// Fourth grouping of elements for Ownership
const groupElementOwner = new GroupElement({
elements: [collNameFieldElement, ownedByFieldElement, maintainFieldElement],
initialState: "collapsed",
// Tree notes field element
const treeNotesFieldElement = new FieldElement({
// autocastable to TextBoxInput
//================================================
// Set the properties for the
// trees feature layer's FormTemplate
//================================================
const treeformTemplate = new FormTemplate({
description: "Redlands trees",
//================================================
//================================================
const webmap = new WebMap({
id: "1e2a951b191c4c4998700ddcf8b46f02",
const view = new MapView({
center: [-117.18, 34.03],
view.map.loadAll().then(() => {
view.map.editableLayers.forEach((layer) => {
// Grab the trees layer from the webmap and set its formTemplate.
if (layer.type === "feature" && layer.title === "Trees") {
layer.formTemplate = treeformTemplate;
const editor = new Editor({
supportingWidgetDefaults: {
groupDisplay: "sequential",
view.ui.add(editor, "top-right");