PracticeVideosBlogFlashcards

9.3 Rule Inputs, Component Parameters & Two-Way Binding

Key Takeaways

  • Rule Inputs (ri!) establish the public parameter interface of a SAIL rule, enabling parent interfaces, process models, and record views to pass data into and receive modified data back from the component.

  • Interactive SAIL components implement two-way data binding through complementary value (read state) and saveInto (write state) parameters.

  • a!save can transform save!value or update several interface state targets; preparing audit fields does not itself persist an audit log.

  • Task edits flow through ac! node inputs and their configured saves to process variables; start forms use process parameters.

  • Modularity best practices dictate building 'pure' UI components that rely strictly on Rule Inputs rather than querying global database entities directly, maximizing component reusability across multiple applications.

Last updated: October 2026

Rule Inputs, Component Parameters & Two-Way Binding

Quick Summary: In Appian SAIL, interfaces communicate with the broader application ecosystem through Rule Inputs (ri!), which define the public data contract of an interface. Interactive components achieve two-way data binding through paired value and saveInto parameters. While value reads and displays the current state, saveInto captures user modifications. By leveraging a!save() and the contextual save!value operator, developers transform data during user interaction and seamlessly return updated records to process models upon task submission.


The Architectural Role of Rule Inputs (ri!)

Every SAIL interface is fundamentally an expression rule that evaluates to a component tree. To make interfaces dynamic and reusable, Appian provides Rule Inputs (ri!) as parameter definitions that define the external interface contract.

+--------------------------------------------------------------------------+
|                      PARENT CONTEXT (Process Model / Site)               |
|                                                                          |
|   Process Variable: pv!customerRecord (Type: Customer CDT)              |
+------------------------------------+-------------------------------------+
                                     | 1. Passes data in (Read Binding)
                                     | 4. Returns data out (Write Binding)
                                     v
+------------------------------------+-------------------------------------+
|                        SAIL INTERFACE CONTRACT                           |
|                                                                          |
|   Rule Input: ri!customer (Type: Customer CDT, Array: False)             |
|                                                                          |
|   +------------------------------------------------------------------+   |
|   | Component: a!textField(                                          |   |
|   |   label: "Customer Name",                                        |   |
|   |   value: ri!customer.name,              <-- 2. Reads from ri!    |   |
|   |   saveInto: ri!customer.name            <-- 3. Writes to ri!     |   |
|   | )                                                                |   |
|   +------------------------------------------------------------------+   |
+--------------------------------------------------------------------------+

Scoping Comparison: ri! vs. local!

  • local! (Local Variables): Internal, private, and ephemeral. Exists solely within the interface during runtime execution. Destroyed when the interface is closed.
  • ri! (Rule Inputs): Explicit inputs supplied by the caller, with persistence determined by that caller and its mappings. Functions as the gateway through which parent callers (such as a Process Model User Input Task, a Record View, or a parent Interface) inject data into the interface and receive updated data back.

Configuring Rule Inputs

When creating Rule Inputs in the Appian Designer interface editor, developers specify three critical attributes:

  1. Name: Standardized in camelCase (e.g., ri!customer, ri!orderItems, ri!isReadOnly).
  2. Type: Can be bound to primitive system types (Text, Integer, Date), Custom Data Types (CDTs), Record Types, or generic types (Any Type).
  3. Array Configuration ("List"): Dictates whether the rule input accepts a single scalar value (Array: false) or a collection of values (Array: true). For instance, a child interface rendering a purchase order line-item grid requires an array rule input of type OrderLineItem.

Two-Way Data Binding: The Mechanics of value and saveInto

Unlike traditional web frameworks that rely on hidden form fields or imperative change listeners, Appian SAIL implements a reactive, two-way data binding architecture. Every interactive input component features two complementary parameters: value and saveInto.

a!textField(
  label: "Primary Contact Phone",
  value: ri!customer.phoneNumber,
  saveInto: ri!customer.phoneNumber,
  required: true
)

The 5-Step Lifecycle of a SAIL User Interaction

  1. Component Rendering (Read Phase): The interface engine evaluates the component's value parameter (ri!customer.phoneNumber). The server translates this into a JSON payload, and the client displays the string "555-0199".
  2. User Modification (Event Phase): The user edits the phone number to "555-0100" and triggers the field's configured save event.
  3. Save Execution (Write Phase): The client transmits the change event to the server. The server executes the expression defined in saveInto, assigning the new value directly into ri!customer.phoneNumber.
  4. Re-Evaluation (Reactivity Phase): The server re-evaluates the interface expression tree to incorporate the newly updated state across all dependent variables.
  5. DOM Update (Render Phase): The client updates the display with the new evaluated state.

Common Data Binding Misconfigurations

ConfigurationUser Experience / System BehaviorRoot Cause
value defined, saveInto omittedThere is no durable interface-state save target; edits may revert on evaluationThe component has a read binding but no write target to capture state
saveInto defined, value omittedUser can type, but entered text vanishes immediately after focus shiftsThe component writes the change to the variable, but does not read it back to display
Mismatched types in value and saveIntoRuntime expression error or type coercion failureAttempting to save a text string into an integer or date variable without casting

Advanced Save Logic with a!save() and save!value

In simple form fields, passing a direct variable reference to saveInto (e.g., saveInto: ri!customer.name) suffices. However, enterprise applications frequently require transforming data before saving, setting automated audit timestamps, or updating multiple related variables simultaneously. Developers accomplish this using a!save().

Syntax of a!save()

a!save(target, value)
  • target: The variable (ri! or local!) where the data must be stored.
  • value: The expression defining what will be written to the target.

The save!value Context Operator

Within the scope of a component's saveInto parameter, Appian provides a special contextual operator: save!value. save!value represents the new raw value generated by the user's interaction (e.g., the exact text entered into a text box, the choice selected in a dropdown, or the date picked in a calendar).

Common Enterprise Save Patterns

Pattern 1: Data Transformation on Input

Enforcing uppercase formatting on a vehicle identification number (VIN) or postal code:

a!textField(
  label: "Vehicle Identification Number (VIN)",
  value: ri!vehicle.vin,
  saveInto: a!save(ri!vehicle.vin, upper(save!value))
)

Pattern 2: Multi-Variable Audit Metadata

When an underwriter modifies an approval limit, the interface prepares the new limit, timestamp, and user identifier in its state; a later persistence operation must save them:

a!floatingPointField(
  label: "Credit Approval Limit",
  value: ri!creditProfile.approvalLimit,
  saveInto: {
    ri!creditProfile.approvalLimit,
    a!save(ri!creditProfile.lastModifiedTimestamp, now()),
    a!save(ri!creditProfile.lastModifiedBy, loggedInUser()),
    a!save(ri!creditProfile.isAuditFlagged, true)
  }
)

Pattern 3: Conditional Save Execution

Conditionally clearing or populating variables based on user selection:

a!dropdownField(
  label: "Payment Method",
  choiceLabels: {"Credit Card", "Bank Transfer", "Cash"},
  choiceValues: {"CC", "ACH", "CASH"},
  value: local!paymentMethod,
  saveInto: {
    local!paymentMethod,
    if(
      save!value = "CASH",
      a!save(local!requiresPhysicalReceipt, true),
      a!save(local!requiresPhysicalReceipt, false)
    )
  }
)

Data Flow Between Interfaces and Process Models

A process start form can receive parameterized process variables through its interface inputs and save submitted values back to those parameters. A User Input Task uses node inputs for editable form data. The reusable interface itself uses ri! inputs rather than directly naming process variables.

For a task, define ac!customer initialized from pv!customer. Map the interface's customer input to ac!customer on the Forms tab. Configure the node input's save or an appropriate node output to update pv!customer when the task completes. Read-only values can be supplied directly for display, but the task form cannot save directly into pv! variables.

BoundaryExample configuration
Process to nodeac!customer initialized from pv!customer
Node to interfacerule!APP_customerForm(customer: ac!customer)
Component editvalue and saveInto target ri!customer.name
Node completionConfigured save returns ac!customer into pv!customer
Durable persistenceSubsequent authorized Write Records or data-store write

Check the actual node input, rule input, and process variable after submitting a test task. A form can appear to work while editing only a local copy. Also refresh mappings when interface inputs change and avoid competing saves into the same process variable.

Submission and Validation

Use a button with submit: true to complete a task form after its saves and validations succeed. A non-submit button can update interface state without advancing the workflow. The validate default follows submit: true for submitting buttons, false otherwise. Configure both deliberately for a wizard or a cancel action.

a!buttonWidget(
  label: "Submit Customer Profile",
  value: "SUBMITTED",
  saveInto: ri!actionTaken,
  submit: true,
  style: "SOLID",
  validate: true
)

A button can set an action result before completing the form. The following process gateway must interpret that result, and an authorization check must still govern the business change. Merely hiding a button or displaying a read-only field does not secure the operation.

Realistic Scenario: Customer Profile Update Interface

An insurance enterprise constructs an interface allowing agents to update policyholder contact records:

  1. Interface Definition: INS_uiUpdateCustomerProfile defines two rule inputs:
    • ri!customer (Type: INS_Customer CDT, Array: false)
    • ri!isUnderwritingReview (Type: Boolean, Array: false)
  2. Component Composition:
    • First Name and Last Name inputs bind value: ri!customer.firstName and saveInto: ri!customer.firstName.
    • Email Address field validates formatting: a separate validation rule that checks the intended email contract; a simple wildcard is not complete email validation.
    • Child Address Interface: The form embeds a modular child interface rule!INS_uiAddressForm(address: ri!customer.address, readOnly: false).
  3. Process Model Integration: In the Review Customer Claim process model, a User Input Task initializes an ac! node input from pv!claimCustomer, maps that node input to ri!customer, and configures its save back into pv!claimCustomer. On submission the modified data reaches the process variable in engine memory, ready for the subsequent Write to Data Store Entity smart service node.

Exam Traps & Binding Guidelines

Warning

Common Exam Trap 1: Attempting to reference pv! variables directly within a SAIL interface. Interfaces do not have access to the pv! namespace. An expression like value: pv!customerName will throw an immediate syntax error. Process data must enter an interface through a Rule Input (ri!) mapped on the task form.

Warning

Common Exam Trap 2: Using save!value outside of the saveInto parameter. save!value is a contextual keyword that is instantiated only during a save event. Calling save!value inside value, validations, or instructions triggers an undefined variable error.

Tip

Design Best Practice: Keep child interfaces 'pure'. A child interface should receive data through its rule inputs and communicate modifications back through those same rule inputs, avoiding direct calls to process smart services or application-specific mutations. This makes the child interface universally embeddable across Sites, Portals, and Record Views.

Source checked 2026-10-18: Appian interface/process mappings.

Test Your Knowledge

A developer attempts to reference pv!assignedAnalyst directly inside a SAIL interface expression. Why does this expression fail to evaluate?

A

Process variables can only be referenced inside a!formLayout() button configurations.

B

Process variables are accessible only when the user holds System Administrator privileges.

C

Process variables must be converted to XML before an interface can parse them.

D

SAIL interfaces do not have access to the pv! namespace; process variables must be mapped to Rule Inputs (ri!) on the task form.

Test Your Knowledge

A developer needs to ensure that whenever a user enters a postal code into a text field, the text is automatically transformed and saved in uppercase letters. Which saveInto configuration achieves this requirement?

A

saveInto: upper(ri!address.postalCode)

B

saveInto: a!save(ri!address.postalCode, upper(save!value))

C

saveInto: { ri!address.postalCode, save!value: upper() }

D

saveInto: a!save(save!value, upper(ri!address.postalCode))

Test Your Knowledge

A paragraph field displays ri!notes but has no saveInto target. What is missing from the data contract?

A

A new process model for every keystroke

B

A requirement that the field become a grid

C

A guarantee that all text will automatically persist to the database

D

A save target that updates interface state and can flow back through the caller mapping

Sections you finish are checked off in the contents.