Interface Tutorial

This tutorial will teach you how to build an interface in Appian, working in both Design Mode and Expression Mode. Completing this tutorial will enable you to move on to the Process Model Tutorial.

Create the Appian Tutorial Application

The Appian Tutorial application is used to contain the design objects created while working through this tutorial.

The tutorial application only needs to be created once. If you have already created the tutorial application, skip the steps below.

To create the Appian Tutorial application

  1. Log in to Appian Designer (for example, myappiansite.com/suite/design).
  2. Click New Application.
  3. In the Name field, type Appian Tutorial.
  4. Optionally, in the Description field, add a short description.
  5. Click Create.

The application contents view displays. Right now the application is empty. Each design object that you create during the course of this tutorial will appear in this list and be associated with the tutorial application.

Create an Interface

To start, we will create an interface named expenseReportForm, and store it in the Examples folder:

  1. Open the Appian Tutorial application.
  2. Click New, and choose Interface.
  3. In the Create Interface dialog, complete the following fields:
    • Leave Create from Scratch selected
    • Enter expenseReportForm in the Name field.
    • For Save In, use the picker to select the Examples folder
  4. Click Create & Edit.

The newly created interface will open in a new tab by default. If you don't see a new tab, check your browser to see if you have pop-ups enabled.

Create Rule Inputs

Each form input saves its value into a variable, so let's add the variables. From the RULE INPUTS pane on the right, create rule inputs by clicking New Interface Input (+ button):

  • expenseItem (Text)
  • expenseDate (Date)
  • expenseAmount (Number (Decimal))
  • cancel (Boolean)

images/sail_tutorial/

Add Components to the Interface

In this section, we'll be adding components to our interface in Design Mode. We'll start by choosing a layout, then adding a few components to it:

  1. From the OUTLINES section of templates on the right, choose FORM.

    images/sail_tutorial/

  2. From the EDIT view, click on the title Form to select the Form Layout.
  3. From the COMPONENT CONFIGURATION pane on the right, replace the Label field value with Submit Expense Report.

    images/sail_tutorial/

  4. Delete the Box Layout and both Section layouts by clicking on them to select them, then from the context menu choose Delete.

    images/sail_tutorial/

    • You should be left with nothing but the form layout and the cancel and submit buttons.

    SAIL_Tutorial_Cleared_Form

  5. From the COMPONENT PALETTE on the left, in the INPUTS section, drag a TEXT component, a DATE component, and a DECIMAL component into the Form Layout.

    images/sail_tutorial/

Configure Each Component

Now that we have the components, let's configure them.

  1. Click on the Text component to select it.
  2. From the COMPONENT CONFIGURATION pane on the right, change the following parameters:
    • For Label enter: Expense Item
    • For Display Value select ri!expenseItem
    • For Save Input To select ri!expenseItem
    • Check Required.

    images/sail_tutorial/

  3. Follow the same steps for the Date field and change the following parameters:
    • For Label enter: Expense Date
    • For Display Value and Save Input To, select ri!expenseDate
    • Check Required.
  4. Follow the same steps for the Decimal field and change the following parameters:
    • For Label enter: Expense Amount
    • For Display Value and Save Input To, select ri!expenseAmount
    • Check Required.
  5. You should see:

    images/sail_tutorial/

Click SAVE. Keep this window open so you can quickly modify the interface as we continue through the following sections.

Conditionally Show Interface Components

A common design requirement is to only display a component based on a condition. Because interfaces are defined using expressions, the full expression library can be used to add dynamic behavior to the interface. For this example, we'll add a paragraph component for entering comments and we will only display it when the expense amount is greater than $100.

  1. From the RULE INPUTS pane on the right, add a new input named comments of type Text.
  2. From the COMPONENT PALETTE on the left, in the INPUTS section, drag a PARAGRAPH component into the form beneath the Expense Amount component.

    images/sail_tutorial/

  3. Change the Label to Comments, set Display Value and Save Input To to the comments input, and make the field required by checking Required.
  4. Under Visibility, check Only show when…, then click Edit Condition.

    images/sail_tutorial/

  5. In the Visibility (Boolean) dialog, enter: ri!expenseAmount > 100

    images/sail_tutorial/

  6. Click OK to close the expression box.

Let's test the conditional display. Enter 101 in the Expense Amount field and click out of the field, or press the Enter key to see the paragraph component displayed.

images/sail_tutorial/

Selecting Hidden Components

Up until now you've been able to select components simply by clicking on them. However, when a component is conditionally visible, you may not be able to see it in order to click on it. Another way you can select it would be from the interface navigation hidden in the right-hand panel. Simply drag the panel to expand it.

nav_highlight

Be sure to save your interface before continuing.

Add a Validation

You can also configure validations for the interface by adding them directly to the expression. For example, let’s add a custom validation to our form that only allows users to enter up to 100 characters in the Comments field. To do this,

  1. Select the Comments paragraph component.
  2. From the COMPONENT CONFIGURATION pane, click the Edit as Expression icon to the right of the Validations field.

    images/sail_tutorial/

  3. Enter the following expression in the Validations (List of Text String) expression box:

    if(len(ri!comments)<=100, null, "Your text has exceeded 100 characters")
    

    SAIL_Tutorial_Comment_Validation_Expression

  4. Click OK to close the expression box.

By modifying this, you can create a variety of other validations based on the content added to the "Comments" field. And since the validations parameter is an array, you can add multiple validations.

To test out the validation, enter more than 100 characters in the Comments field and click out of the field; you should your validation message.

SAIL_Tutorial_Comment_Validation_Message

Add a Reusable Component to Your Interface

In this section, we'll learn about reusability and how interface components can be represented with the Appian expression language.

There are many cases where you'll want to reuse part or all of an interface. For example, you may have several forms that have a conditional Comments field. Rather than re-create it on every interface we can move that paragraph field into a new interface object and simply call it, which is what we're going to do.

First we'll want to get the expression that represents the Comments field.

  1. Select the Comments field, then from the COMPONENT CONFIGURATION pane, click the Edit as Expression button.

    images/sail_tutorial/

    • You should now see the expression to copy.

      SAIL_Tutorial_Edit_Icon_Header_Expression

  2. From the application contents view of the Appian Tutorial application, create a new interface called enterComments and save it in the Examples folder.

  3. Add the following inputs to the new enterComments interface.
    • showWhen (Boolean)
    • labelPosition (Text)
    • readOnly (Boolean)
    • required (Boolean)
    • commentValue (Text)
    • commentSaveInto (Text Array)
  4. Drag a PARAGRAPH component and drop it onto the EDIT view.

  5. Select the Paragraph component and from the COMPONENT CONFIGURATION pane, click the Edit as Expression button.

  6. Replace the contents of the expression with the Comments field expression, then make the below, highlighted changes to the expression:

    a!paragraphField(
    label: "Comments",
    labelPosition: `ri!labelPosition`,
    value: `ri!commentValue`,
    saveInto: `ri!commentSaveInto`,
    refreshAfter: "UNFOCUS",
    showWhen: `ri!showWhen`,
    required: `ri!required`,
    height: "MEDIUM",
    !  readOnly: ri!readOnly,
    validations: if(len(ri!commentSaveInto)<=100, null, "Your text has exceeded 100 characters")
    )
    
  7. Click OK.

  8. Save the interface.

Return to the the Submit Expense Report (expenseReportForm) interface. Now you will modify this interface to call the new one you just created.

  1. Select the Comments field and from the COMPONENT CONFIGURATION pane, click the Edit as Expression button.

  2. Replace the existing expression with the following:

    rule!enterComments(
      showWhen: ri!expenseAmount > 100,
      labelPosition: "ABOVE",
      readOnly: false,
      required: true,
      commentValue: ri!comments,
      commentSaveInto: ri!comments
    )
    
  3. Click OK.

You should see the following:

images/sail_tutorial/

Note that your Comments field is now calling enterComments.

Make Your Interface Conditionally Read-Only

In this section we'll learn more about reusability, dynamic interface behavior, interface testing, and working in Expression Mode.

Now that we have a form to submit an expense report, we will need an interface so that an approver can review the expense report. The only difference in between interfaces is that the one for the approval will need to be read-only and contain an additional input so they can approve or reject the expense report.

First, we'll add the new field for approval.

  1. Add the following inputs to the interface.
    • step (Text)
    • approve (Boolean)
  2. From the SELECTION section of the palette, drag a RADIO BUTTONS component beneath the Comments field.
  3. Select Radio Buttons.
  4. Set the following paramaters:
    • Label Position: HIDDEN
    • Selected Value: ri!approve
    • Save Selection To: ri!approve
    • Required
  5. Under Choice Labels, click List of Text.
    • Enter Approve for the first option.
    • Enter Reject for the second option.
    • Click the Radio Buttons back link.

      images/sail_tutorial/

  6. Under Choice Values, click List of Any Type.
    • Click 1, then replace that value with true.
    • Click OK.
    • Click 2, then replace that value with false.
    • Click OK.

Now, to configure the conditional requiredness of the fields click </> EXPRESSION MODE from the title bar. This replaces the Component Palette with the Interface Definition, where you can see the entire expression that defines the interface.

SAIL_Tutorial_Expression_View_Button.png

In this mode you'll add two local variables, which are interface-specific values. Local variables are initialized when the interface loads, so next we'll put the entire interface definion into a load() function and add the local variables.

  1. Modify the interface definition by making the below, highlighted changes to the expression:

    +    load(
    +      local!readOnly: ri!step="APPROVAL",
    +      local!labelPosition: if(local!readOnly, "ADJACENT", "ABOVE"),
          a!formLayout(
            label: "Submit Expense Report",
            contents: {
              a!textField(
                label: "Expense Item",
                labelPosition: "ABOVE",
                value: ri!expenseItem,
                saveInto: ri!expenseItem,
                refreshAfter: "UNFOCUS",
                required: true,
                validations: {},
                align: "LEFT"
              ),
              a!dateField(
                label: "Expense Date",
                labelPosition: "ABOVE",
                value: ri!expenseDate,
                saveInto: ri!expenseDate,
                required: true,
                validations: {},
                align: "LEFT"
              ),
              a!floatingPointField(
                label: "Expense Amount",
                labelPosition: "ABOVE",
                value: ri!expenseAmount,
                saveInto: ri!expenseAmount,
                refreshAfter: "UNFOCUS",
                required: true,
                validations: {},
                align: "LEFT"
              ),
              rule!enterComments(
                showWhen: ri!expenseAmount > 100,
                labelPosition: "ABOVE",
                readOnly: false,
                required: true,
                commentValue: ri!comments,
                commentSaveInto: ri!comments
              ),
              a!radioButtonField(
                labelPosition: "COLLAPSED",
                choiceLabels: {"Approve", "Reject"},
                choiceValues: {true, false},
                value: ri!approve,
                saveInto: ri!approve,
                required: true
              )
            },
            buttons: a!buttonLayout(
              primaryButtons: {
                a!buttonWidget(
                  label: "Submit",
                  style: "PRIMARY",
                  submit: true
                )
              },
              secondarybuttons: {
                a!buttonWidget(
                  label: "Cancel",
                  style: "SECONDARY",
                  value: true,
                  saveInto: ri!cancel,
                  submit: true,
                  validate: false
                )
              }
            )
          )
    + )
    
  2. Use the new local!readOnly variable to make each component conditionally read-only and optional when it is in read-only mode. Use the new local!labelPosition to control each component's label position (except where the label position is COLLAPSED). Modify your expression with the following:

    load(
      local!readOnly: ri!step="APPROVAL",
      local!labelPosition: if(local!readOnly, "ADJACENT", "ABOVE"),
      a!formLayout(
        label: "Submit Expense Report",
        contents: {
          a!textField(
            label: "Expense Item",
            labelPosition: `local!labelPosition`,
            value: ri!expenseItem,
            saveInto: ri!expenseItem,
            refreshAfter: "UNFOCUS",
            required: `not(local!readOnly)`,
    +       readOnly: local!readOnly,
            validations: {},
            align: "LEFT"
          ),
          a!dateField(
            label: "Expense Date",
            labelPosition: `local!labelPosition`,
            value: ri!expenseDate,
            saveInto: ri!expenseDate,
            required: `not(local!readOnly)`,
    +       readOnly: local!readOnly,
            validations: {},
            align: "LEFT"
          ),
          a!floatingPointField(
            label: "Expense Amount",
            labelPosition: `local!labelPosition`,
            value: ri!expenseAmount,
            saveInto: ri!expenseAmount,
            refreshAfter: "UNFOCUS",
            required: `not(local!readOnly)`,
    +       readOnly: local!readOnly,
            validations: {},
            align: "LEFT"
          ),
          rule!enterComments(
            showWhen: ri!expenseAmount > 100,
            labelPosition: `local!labelPosition`,
            readOnly: `local!readOnly`,
            required: `not(local!readOnly)`,
            commentValue: ri!comments,
            commentSaveInto: ri!comments
          ),
          a!radioButtonField(
            labelPosition: "COLLAPSED",
            choiceLabels: {"Approve", "Reject"},
            choiceValues: {true, false},
            value: ri!approve,
            saveInto: ri!approve,
            required: true
          )
        },
        buttons: a!buttonLayout(
          primaryButtons: {
            a!buttonWidget(
              label: "Submit",
              style: "PRIMARY",
              submit: true
            )
          },
          secondarybuttons: {
            a!buttonWidget(
              label: "Cancel",
              style: "SECONDARY",
              value: true,
              saveInto: ri!cancel,
              submit: true,
              validate: false
            )
          }
        )
      )
    )
    
  3. Only display the radio button when on the approval step by modifying your expression with the following:

    load(
      local!readOnly: ri!step="APPROVAL",
      local!labelPosition: if(local!readOnly, "ADJACENT", "ABOVE"),
      a!formLayout(
        label: "Submit Expense Report",
        contents: {
          a!textField(
            label: "Expense Item",
            labelPosition: local!labelPosition,
            value: ri!expenseItem,
            saveInto: ri!expenseItem,
            refreshAfter: "UNFOCUS",
            required: not(local!readOnly),
            readOnly: local!readOnly,
            validations: {},
            align: "LEFT"
          ),
          a!dateField(
            label: "Expense Date",
            labelPosition: local!labelPosition,
            value: ri!expenseDate,
            saveInto: ri!expenseDate,
            required: not(local!readOnly),
            readOnly: local!readOnly,
            validations: {},
            align: "LEFT"
          ),
          a!floatingPointField(
            label: "Expense Amount",
            labelPosition: local!labelPosition,
            value: ri!expenseAmount,
            saveInto: ri!expenseAmount,
            refreshAfter: "UNFOCUS",
            required: not(local!readOnly),
            readOnly: local!readOnly,
            validations: {},
            align: "LEFT"
          ),
          rule!enterComments(
            showWhen: ri!expenseAmount > 100,
            labelPosition: local!labelPosition,
            readOnly: local!readOnly,
            required: not(local!readOnly),
            commentValue: ri!comments,
            commentSaveInto: ri!comments
          ),
          a!radioButtonField(
            labelPosition: "COLLAPSED",
            choiceLabels: {"Approve", "Reject"},
            choiceValues: {true, false},
            value: ri!approve,
            saveInto: ri!approve,
            required: true`,`
    +       showWhen: local!readOnly
          )
        },
        buttons: a!buttonLayout(
          primaryButtons: {
            a!buttonWidget(
              label: "Submit",
              style: "PRIMARY",
              submit: true
            )
          },
          secondarybuttons: {
            a!buttonWidget(
              label: "Cancel",
              style: "SECONDARY",
              value: true,
              saveInto: ri!cancel,
              submit: true,
              validate: false
            )
          }
        )
      )
    )
    
  4. Hide the cancel button on the approval step by modifying your expression with the following:

    load(
      local!readOnly: ri!step="APPROVAL",
      local!labelPosition: if(local!readOnly, "ADJACENT", "ABOVE"),
      a!formLayout(
        label: "Submit Expense Report",
        contents: {
          a!textField(
            label: "Expense Item",
            labelPosition: local!labelPosition,
            value: ri!expenseItem,
            saveInto: ri!expenseItem,
            refreshAfter: "UNFOCUS",
            required: not(local!readOnly),
            readOnly: local!readOnly,
            validations: {},
            align: "LEFT"
          ),
          a!dateField(
            label: "Expense Date",
            labelPosition: local!labelPosition,
            value: ri!expenseDate,
            saveInto: ri!expenseDate,
            required: not(local!readOnly),
            readOnly: local!readOnly,
            validations: {},
            align: "LEFT"
          ),
          a!floatingPointField(
            label: "Expense Amount",
            labelPosition: local!labelPosition,
            value: ri!expenseAmount,
            saveInto: ri!expenseAmount,
            refreshAfter: "UNFOCUS",
            required: not(local!readOnly),
            readOnly: local!readOnly,
            validations: {},
            align: "LEFT"
          ),
          rule!enterComments(
            showWhen: ri!expenseAmount > 100,
            labelPosition: local!labelPosition,
            readOnly: local!readOnly,
            required: not(local!readOnly),
            commentValue: ri!comments,
            commentSaveInto: ri!comments
          ),
          a!radioButtonField(
            labelPosition: "COLLAPSED",
            choiceLabels: {"Approve", "Reject"},
            choiceValues: {true, false},
            value: ri!approve,
            saveInto: ri!approve,
            required: true,
            showWhen: local!readOnly
          )
        },
        buttons: a!buttonLayout(
          primaryButtons: {
            a!buttonWidget(
              label: "Submit",
              style: "PRIMARY",
              submit: true
            )
          },
          secondarybuttons: {
            a!buttonWidget(
              label: "Cancel",
              style: "SECONDARY",
              value: true,
              saveInto: ri!cancel,
              submit: true,
              validate: false`,`
    +         showWhen: not(local!readOnly)
            )
          }
        )
      )
    )
    
  5. Change the form title based on the step by modifying your expression with the following:

    load(
      local!readOnly: ri!step="APPROVAL",
      local!labelPosition: if(local!readOnly, "ADJACENT", "ABOVE"),
      a!formLayout(
        label: `if(local!readOnly, "Approve Expense Report", "Submit Expense Report")`,
        contents: {
          a!textField(
            label: "Expense Item",
            labelPosition: local!labelPosition,
            value: ri!expenseItem,
            saveInto: ri!expenseItem,
            refreshAfter: "UNFOCUS",
            required: not(local!readOnly),
            readOnly: local!readOnly,
            validations: {},
            align: "LEFT"
          ),
          a!dateField(
            label: "Expense Date",
            labelPosition: local!labelPosition,
            value: ri!expenseDate,
            saveInto: ri!expenseDate,
            required: not(local!readOnly),
            readOnly: local!readOnly,
            validations: {},
            align: "LEFT"
          ),
          a!floatingPointField(
            label: "Expense Amount",
            labelPosition: local!labelPosition,
            value: ri!expenseAmount,
            saveInto: ri!expenseAmount,
            refreshAfter: "UNFOCUS",
            required: not(local!readOnly),
            readOnly: local!readOnly,
            validations: {},
            align: "LEFT"
          ),
          rule!enterComments(
            showWhen: ri!expenseAmount > 100,
            labelPosition: local!labelPosition,
            required: not(local!readOnly),
            readOnly: local!readOnly,
            commentValue: ri!comments,
            commentSaveInto: ri!comments
          ),
          a!radioButtonField(
            labelPosition: "COLLAPSED",
            choiceLabels: {"Approve", "Reject"},
            choiceValues: {true, false},
            value: ri!approve,
            saveInto: ri!approve,
            required: true,
            showWhen: local!readOnly
          )
        },
        buttons: a!buttonLayout(
          primaryButtons: {
            a!buttonWidget(
              label: "Submit",
              style: "PRIMARY",
              submit: true
            )
          },
          secondarybuttons: {
            a!buttonWidget(
              label: "Cancel",
              style: "SECONDARY",
              value: true,
              saveInto: ri!cancel,
              submit: true,
              validate: false,
              showWhen: not(local!readOnly)
            )
          }
        )
      )
    )
    

Now let's test the read only behavior of the interface.

  1. Click TEST from the title bar. This pulls up the Test Inputs dialog. Enter the Value for the following inputs:
    • expenseItem (Text): Team Lunch
    • expenseDate (Date): 05/03/2017
    • expenseAmount (Number (Decimal)): 120.87
    • comments (Text): Team lunch for 6 people.
    • step (Text): APPROVAL

    SAIL_Tutorial_Test_Read_Only.png

  2. Click Test Interface to check out the new read-only behavior.

You should see the following:

images/sail_tutorial/

Congratulations! You're ready to move on to other tutorials.

FEEDBACK