This blog focuses on replicating the Smart Editor widget functionality using Smart Forms. For new examples, see Go Beyond the Smart Editor using Smart Forms.
As you migrate from Web AppBuilder to leverage Experience Builder, you’ll be moving from the Smart Editor widget to the Editor widget.
Use the Editor widget with Smart Forms to improve your editing experiences and automatically calculate attributes. This widget is available in Map Viewer and app builders such as Experience Builder and Instant Apps.
To build Forms in Map Viewer, all you need is an editable layer. If this is your first time, read this blog to get started.
To replicate the Smart Editor widget functionality, use Calculated expressions and the Arcade scripting language.
Now, if you’re like me, you may turn pale at the thought of having to write lines of code. This is why I’m providing some samples to help you get started.
Attribute Actions
In the Smart Editor widget, Intersection, Preset, Coordinate and Address Attribute Actions can be set to populate default values or to calculate values on the fly across different layers using location information.
The examples in this blog can also be found in this sample web map. To view them in action, open the Edit widget and create a New Feature on the map.
Intersection
In the Smart Editor widget, Intersection Attribute Actions allow you to populate an attribute field based on a field value sourced from intersecting layers in the map.
Use the Intersects geometry function from the Arcade library to recreate this functionality using calculated expression in the Smart Forms.
Fetch an attribute value from an intersecting feature
Example: Copy the Zip code attribute value from the USA ZIP Code Boundaries layer into the corresponding field of the layer being edited.
In the Smart Editor widget, you can select the source layer and field to extract a value and define the target fields to apply the extracted value.
With Smart Forms, write an expression to define the source layer, the intersection function, and the target layer. See the sample web map.
Click here to show the sample code
Copy the code below to create your own calculations:
// Step 1. Define the source layer and fields to intersect./
// To customize below, replace the layer and field names.
var source_layer = FeatureSetByName($map,"USA ZIP Code Boundaries", ['ZIP_CODE'])
// Step 2. Intersect the current location with the source layer and get the field value.
// If there are multiple intersecting features, get the value from the feature that is
// first returned by the query.
var get_value = First(Intersects($feature, source_layer))
// Step 3. If the current location intersects the source layer, return the value from the source field.
// Otherwise return null.
// To customize below replace the field name.
if (!IsEmpty(get_value)) {
return get_value['ZIP_CODE']
}
Fetch an attribute value from a nearby feature (closest)
Example: Find the closest Bus Stop point feature (within a 1,000 feet buffer).
Write a Calculated expression to define the source layer, buffer the current location, and use the intersection function to find the closest feature. See the sample web map.
Click here to show the sample code
Copy the code below to create your own calculations:
// Get the bus stops layer
// To customize, replace the layer name below
var busStops = FeatureSetByName($map,"RTD Active Bus Stops")
// Buffer the current location and intersect with the bus stops
var bufferedLocation = Buffer($feature, 1000, 'feet')
var candidateStops = Intersects(busStops, bufferedLocation)
// Calculate the distance between the bus stops and the current location
// Store the feature and distance as a dictionary and push it into an array
var featuresWithDistances = []
for (var f in candidateStops) {
Push(featuresWithDistances,
{
'distance': Distance($feature, f, 'feet'),
'feature': f
}
)
}
// Sort the candidate bus stops by distance using a custom function
function sortByDistance(a, b) {
return a['distance'] - b['distance']
}
var sorted = Sort(featuresWithDistances, sortByDistance)
// Get the closest bus stop
var closestFeatureWithDistance = First(sorted)
// If there was no bus stop, return null
if (IsEmpty(closestFeatureWithDistance)) { return null }
// Return the bus stop name attribute value
// To customize, replace the field name "STOPNAME" below
return `${closestFeatureWithDistance['feature']['STOPNAME']}`
Fetch an attribute value from an intersecting feature based on layer priority
Example: Get an attribute value from intersecting and overlapping polygon features based on the priority defined for their parent layer.
Write an expression to define the priority (or ranking) for each target layer, intersect the current location with each layer, and determine which value to prioritize. See the sample web map.
Click here to show the sample code
Copy the code below to create your own calculations:
// Step 1. Get the layers from the map
// You can use FeatureSetById or FeatureSetByName
var layer_priority1 = FeatureSetById($map, /* Priority 1 */ "185a34dd0ee-layer-13");
var layer_priority2 = FeatureSetById($map, /* Priority 2 */ "185a34dd0ee-layer-12");
var layer_priority3 = FeatureSetById($map, /* Priority 3 */ "185a34dd0ee-layer-11");
// Step 2. Intersect the current location with the layers
var priority1Features = Intersects(layer_priority1, $feature);
var priority2Features = Intersects(layer_priority2, $feature);
var priority3Features = Intersects(layer_priority3, $feature);
// Step 3. If there are multiple intersecting features per layer,
// get the value from the feature that is first returned by the query
var a = First(priority1Features);
var b = First(priority2Features);
var c = First(priority3Features);
// Step 4. Define the fields and order to get the values from
// intersecting features based on their layer priority.
// To customize, replace the field names "Priority1", "Priority2",
// and "Priority3" below.
When(
!IsEmpty(a), a.Priority1,
!IsEmpty(b), b.Priority2,
!IsEmpty(c), c.Priority3,
"No Intersection"
);
Preset
In the Smart Editor widget, Preset Attribute Actions allow you to prepopulate fields with attribute values when creating new features. This facilitates consistent and rapid data collection.
Use the Date functions from the Arcade library and very simple expressions to recreate this functionality in the Smart Forms. See the sample web map for the examples below.
Hardcoded value
If no default value was defined at the schema level for a field, you can still define it using a very simple expression. For example:
// Simply specify the value to be written into
// the attribute field using quotes.
"This is a hard coded text value."
Date values
Current Date and Time
Preset an attribute value to provide the current date and time at the moment the feature is created and updated. For example:
// Use the Now() function to dynamically get the current date and time
Now()
Relative Date and Time
Preset an attribute value to provide a date and time relative to another date and time value, in the past or the future. For example, here’s how to define the date and time 2 days from the moment the feature is created or updated:
// Step 1. Define the start date
var startDate = Now();
// Step 2. Define how much time to add to the start date
var twoDaysLater = DateAdd(startDate, 2, 'days');
// Step 3. Return the resulting date
return twoDaysLater;
Use a filtered value as a preset value
In Web AppBuilder, you can set up an interaction between the Group Filter widget and the Smart Editor widget to use the filtered value as a default value for attribute fields when you create new features. View this post to learn how to recreate this functionality using a calculated expression: Use the filtered value as a default field value – Esri Community
Coordinates
In the Smart Editor widget, Coordinate Attribute Actions allow you to populate a field based on coordinates. Coordinates can be latitude-longitude, the map’s spatial reference, or Military Grid Reference System (MGRS).
Example: Get the latitude-longitude coordinates when I create and update the geometry of a point feature.
To achieve this with Arcade, you’ll need to write a custom function to convert meters to latitude and longitude, and use the Geometry function from the Arcade library.
Click here to show the sample code
Copy the code below to create your own calculations:
// The expression below if only applicable if the basemap is
// using the Web Mercator projection.
// Create a function to convert meters to lat, long
// Source: http://www.maptiler.org/google-maps-coordinates-tile-bounds-projection/
function MetersToLatLon(geom) {
if (IsEmpty(geom)) {
return [null, null]
}
var originShift = 2.0 * PI * 6378137.0 / 2.0
var lon = (geom.x / originShift) * 180.0
var lat = (geom.y / originShift) * 180.0
lat = 180.0 / PI * (2.0 * Atan( Exp( lat * PI / 180.0)) - PI / 2.0)
return [Round(lat, 6), Round(lon, 6)]
}
// Call the function and return the latitude and longitude values
MetersToLatLon(Geometry($feature))[0] + ", " + MetersToLatLon(Geometry($feature))[1]
Address
In the Smart Editor widget, Address Attribute Actions allow you to populate a field based on an address at a specified location using a locator service.
Example: Fetch the matching address value at the location of the point feature when it’s created or its geometry is updated.
Reverse Geocoding is not yet available using Calculated expressions in Smart Forms.
Possible workaround: use an Intersects function to fetch the nearest feature, in the case of address points.
Smart Actions
In the Smart Editor widget, Smart Actions use custom expressions to define the behavior applied to an attribute field such as being required, hidden, or disabled.
When configuring a Form, you can set dynamic behavior to make fields Editable (or Read-only), Required, and Visible (or hidden).
Set Dynamic Behavior
To achieve this in Smart Forms, you can either build a simple condition expression using the dropdown options or write a custom one in the Arcade editor.
Example: Hide a field if it’s empty. The field in this example only takes in values from the nearest intersecting feature determined with a Calculated expression. The value will be empty if there is no intersecting feature.
Using Smart Forms in App Builders
Although Smart Forms can be created with form builders in Map Viewer or Field Maps, they are not limited to these products. Since Forms are stored in the web map, you can also use them in Experience Builder, Instant Apps, and custom apps you build using the Editor widget.
Experience Builder
Below is an app configuration example using the sample web map from this blog.
Instant Apps
Below is an app configuration example of the Sidebar template using the sample web map from this blog.
Get Started
Use the samples provided in this blog to build Smart Forms and improve your editing experiences.
To learn how to configure Forms and add Calculated expressions, read this blog and refer to the documentation.
Looking for more expression examples? Check out Go Beyond the Smart Editor using Smart Forms, and this growing arcade-expression GitHub repository.
Article Discussion: