Attributes and attachments represent intelligent information about geographic features in a GIS. Although the information captured is important, collecting attribute values can be challenging. For example, attribute value errors can occur when an incorrect value is recorded within the attribute field or when a field is missing a value. Misspelled words and other typographical errors are common problems that can lead to inaccurate conclusions when performing a GIS analysis while solving a spatial problem. Attribute domains, subtypes, rules, and contingent values provide potential enhancements to reduce data entry error. Forms provide another tool that can be used to enhance the data collection process to:
- ease data entry for application end users
- limit the types of input by restricting and guiding data types (for example, character limits, value options, numeric vs. string values)
- perform easier calculations from attribute to attribute using Arcade scripting calculated upon data entry
- simplify adding, updating, and removing attachments
You can display these forms, also known as feature forms, to users of your ArcGIS Maps SDK for .NET application using a couple of approaches.
- The recommended approach is to use the open source toolkit component that uses the API described in this topic.
- For requirements that go beyond the capabilities of the toolkit component, you can work with the API directly as described in this topic to meet your specific obligations.
Build and access the feature form
Forms are defined in the web map JSON specification (also known as formInfo) and created using the map viewer in ArcGIS Online, ArcGIS Enterprise, or the Field Maps Designer. Before you can get started with the API, a form must be defined for a supported feature layer and saved with a web map that you have access to because the form configuration JSON, formInfo, is saved as part of the web map definition. See Display a web map for documentation on how to programmatically access a web map from a portal, if necessary. After programmatically retrieving the web map and obtaining the feature layers contained within, there are two ways you can access the feature form definition: FeatureLayer
or ArcGISFeatureTable
. Both classes contain a property allowing you to retrieve the feature form definition. A FeatureFormDefinition
is the serialized JSON representation of the feature form as it was defined and saved in the web map for a feature layer. Essentially, the feature form definition represents metadata describing the details around how a form was defined.
// Gets a feature layer from the ArcGISFeature to retrieve the feature form definition.
var layer = feature?.FeatureTable?.Layer as FeatureLayer;
// Uses the feature layer to retrieve the feature form definition.
var formDefinition = layer?.FeatureFormDefinition;
if (formDefinition == null) { return; }
A FeatureForm
is created using the feature form definition and a new or existing feature. Using the metadata from the feature form definition and the field information from the feature, the feature form is exposed by the API. As a start, you can use the feature form, for instance, to retrieve the FeatureForm.Elements
defined in the form. FormElement
can be used to help you interrogate the details around a specific element in order to construct a user interface for editing a feature's attributes or attachments. For example, the AttachmentsFormElement
specifies whether you can add, rename, or delete attachments on the feature. There are many methods you can use to obtain the ArcGISFeature
, such as adding a new feature, querying for a feature, or identifying a feature, to name a few.
var featureForm = new FeatureForm(feature, formDefinition);
Evaluate expressions
ArcGIS Arcade can be used to add logic to a form allowing the behavior of form elements to be controlled. With Arcade expressions, form elements can be dynamically hidden, required, or enabled for editing depending on conditions set in the expression. In addition, it's possible to add calculation expressions that allow values to be calculated and populated in the form. The feature form can asynchronously evaluate all form expressions, FeatureForm.EvaluateExpressionsAsync()
, and upon completion return a set of FormExpressionEvaluationException
s providing diagnostic information about errors encountered while evaluating these expressions.
// Call EvaluateExpressionsAsync() before displaying the feature form to get initial expression results.
var formExpressionEvaluationErrors = await featureForm.EvaluateExpressionsAsync();
Retrieve validation errors
Validation errors can be thought of in two distinct ways. The first use case allows you to retrieve validation errors for any individual FieldFormElement
. This approach can be useful to provide your application user with feedback while editing a single form element.
foreach(var validationError in fieldFormElement.ValidationErrors)
{
Console.WriteLine($"Validation error for field '{fieldFormElement.FieldName}':{validationError.Message}");
}
The second use case comes when the user of your application is ready to submit the edited attribute values with the intention of committing the edits to the database. In this scenario, it is ideal to retrieve validation errors for all the form elements in the feature form, although it is not a requirement. If desired, you can use FeatureForm.ValidationErrors
to retrieve the errors. If errors are present, then a dictionary mapping from the field form element's field name (the key) to an array of errors is available to help you determine which fields may have one one or more errors associated with it. An empty dictionary implies that that all form element values are valid, thus the edits can be committed to the database in this scenario.
// Get any validation errors for all form elements.
var messageBldr = new StringBuilder("");
foreach (KeyValuePair<string, IEnumerable<Exception>> err in featureForm.ValidationErrors)
{
// The key is the field name that has one or more validation errors.
messageBldr.AppendLine($"Field " + err.Key + ": " + string.Join(",", err.Value));
}
var validationErrorMsg = messageBldr.ToString();
// TODO: show the validation errors to the user.
If any validation errors exist, then it becomes a matter of choice whether to display these errors or to wait till the user of the application edits form element values. The toolkit component discussed previously, upon displaying the feature form for the first time, does not display validation errors; however, the alternative approach can be taken and any validation errors can be displayed for the application end user to address.
Show any validation errors
In the case that you want to display validation errors that your user will need to address before they can submit the form for further action, the user interface experience is left entirely up to your preferences. It's important to note that the validation errors you learned how to retrieve earlier are likely not sufficient for helping your user resolve the problem. You will likely need additional logic in your application to interrogate why an error was thrown and what message you will display to your user. For example, if the FeatureFormExceedsMaximumLengthException
is retrieved, then your logic could interrogate what the maximum length is when displaying your error message to the user, perhaps even provide a character count as the user addresses the problem that disappears when the character count falls below the maximum length allowed. If necessary, the toolkit component is open source and can be reviewed for examples on how you might address each error type.
Update value for a form element
The user of your application can edit a form element value. When that value is changed, you'll need to update the value on the form element in the API. Calling FieldFormElement.UpdateValue()
updates the value of a field in the feature associated with the FieldFormElement
. At a minimum, the value passed in should be the correct data type for the field in the feature.
fieldFormElement.UpdateValue(textValue);
await featureForm.EvaluateExpressionsAsync();
After the value is updated, you should evaluate expressions in order to update any dependent properties. If you have code that is retrieving validation errors (either at the feature form level or individual field form elements - see retrieve validation errors for details), then your logic will be called if there is any change to the validation status. This process repeats itself for each form element that is editing by the application user.
Process validation errors
Earlier, it was illustrated how to retrieve validation errors. This section describes the process for dealing with validation errors when they exist. If no validation errors exist, then you can proceed with adding a new or updating an existing feature following the edit process captured here. When validation errors exist, you can choose to ignore them and proceed with the editing process or you can work through each validation error by displaying a useful message to your end user and following the process captured in the update value for a form element section since it will be the application user that needs to resolve whatever validation error is not being met.
For example, let's say a form definition contains a field form element used to capture a social security number (SSN). A SSN, for those that may not know, is a unique numerical identifier assigned to every U.S. citizens to track income and other government benefits. In this scenario, this feature attribute is required and all SSN's are precisely 9 digits long. During the design of the form, two constraints are placed on the field form element requiring that the value be present and exactly 9 characters long (minimum = maximum = 9). Initially, this form element can be empty with no error. However, when the form is displayed and the user starts to engage with the field form element, then the following logic can be used to gather the errors for this field form element.
foreach(var validationError in fieldFormElement.ValidationErrors)
{
Console.WriteLine($"Validation error for field '{fieldFormElement.FieldName}':{validationError.Message}");
}
The following code shows one approach on how you might process the collection of potential errors possible in the scenario described.
// The value is a string for this element and field type.
var currentValue = fieldFormElement?.Value?.ToString();
if (string.IsNullOrEmpty(currentValue))
{
if (validationErrors.OfType<FeatureFormFieldIsRequiredException>().Any() &&
validationErrors.OfType<FeatureFormLessThanMinimumLengthException>().Any()) {
// Don't display an error message if the field is empty, which is common.
}
}
else
{
// Show an appropriate message so the user can address the validation error.
}