Interface Tutorial

Overview

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.

The interface we're creating in this tutorial is for a simple expense management application. This application needs a form that lets employees report their expenses, and a form that lets managers review those expenses and approve or reject them. We'll create and configure a single interface that serves both purposes at different times in the workflow.

Create the Appian Tutorial Application

The Appian Tutorial application is used throughout Appian tutorials. Skip the steps in this section if you've already created this application in another tutorial.

To begin with, we need to create an application to contain our design objects.

We will be creating the Appian Tutorial application for this tutorial. All of Appian's tutorials use the Appian Tutorial application as the reference application. After completing this tutorial, you can reuse this application as you work through other Appian tutorials.

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 Create New Application dialog, configure the following properties:

    Property Description
    Name Enter Appian Tutorial.
    Prefix Keep the default prefix, AT, which Appian constructs using the initial characters of each word you entered in the Name parameter. We'll be following the recommended naming standard, and using this short, unique prefix whenever we name an object in this application.
    Description Leave blank. It's normally a best practice to add descriptions to all design objects. However, to save a little time during this tutorial, we'll skip adding descriptions unless the description displays to the end user.
    Generate groups and folders to secure and organize objects Keep this checkbox selected, so that Appian will automatically generate standard groups and folders and assign default security groups for this application.
  4. Click CREATE.
  5. In the Review Application Security dialog, keep the default security settings. Because we selected the Generate groups and folders option in the previous step, Appian automatically uses the AT Users and AT Administrator groups it generated to set our application security appropriately.

    The security of the application object is unrelated to the security of each of the objects contained within the application. This means that you will need to set security permissions for every object in an application in addition to the application object itself. For more information about security permissions for the application object, see Application Security.

  6. Click SAVE. The application view appears.

    screenshot of the application view

Right now, the application contains the folders and groups Appian generated automatically. 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

Now that we've created an application, let's get started on the interface.

We're going to create an interface named AT_expenseReportForm and configure the form to allow users to input values.

Create an interface object

To create an interface object:

  1. In the application view, click New > Interface.
  2. In the Create Interface dialog, configure the following properties:
    • Leave Create from Scratch selected.
    • For Name, enter AT_expenseReportForm.
    • For Save In, select the AT Rules & Constants folder that Appian generated.
  3. Click CREATE.

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

By default, the new interface is in DESIGN MODE. That's where we want to be for now.

Select a template

Next, let's select an interface template, so we can take advantage of functionality already configured in the template.

In the Select a Template panel, under FORMS, click One Column Form.

images/sail_tutorial/

The template you selected opens in the EDIT view.

Change the form title

Now, let's change the form title, so that users viewing the published interface know they're in the right place to submit their expenses.

To change the form title:

  1. Click on the title Form to select the Form Layout component. This action opens the component properties for editing in the the COMPONENT CONFIGURATION pane on the right.
  2. In the COMPONENT CONFIGURATION pane, replace the default Label value with Submit Expense Report.

    images/sail_tutorial/

Remove the sections

The template we selected contains sections, but we don't need them for this particular interface. We'll take those components out to make room for the components we'll add later.

Delete both Section Layout components. For each component:

  1. Click a section to select it.
  2. Open the context menu.
  3. Select Delete.

    SAIL_Tutorial_Remove_Default_Contents

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

SAIL_Tutorial_Cleared_Form

Create rule inputs

Before we add new interface components, let's add the rule inputs needed to configure properties for those components. A rule input is a variable that stores inputs from the interface, so those inputs can be successfully passed to a process model.

For each rule input we need, repeat the following steps:

  1. In the RULE INPUTS pane on the right, click New Rule Input (+ button).
  2. Configure the following properties:

    Name Type Array
    expenseItem Text leave unselected
    expenseDate Date leave unselected
    expenseAmount Number (Decimal) leave unselected

    Because we used an interface template, the cancel rule input has already been added and configured for this interface.

  3. Click CREATE.

Your finished rule inputs should look like this:

images/sail_tutorial/

Add components

Now, we'll add the components to the interface that support user input of expense data.

To add components to the interface, drag the following components from the PALETTE panel into the form layout:

  • a TEXT component
  • a DATE component
  • a DECIMAL component

Your interface should look like this:

images/sail_tutorial/

Configure the new components

Next, we'll configure the components so that the form inputs connect to the rule inputs. We'll also specify whether a specific form input is required to submit the form.

For each of the components, perform the following steps:

  1. Click a component to select it.
  2. In the COMPONENT CONFIGURATION pane, configure the following properties:

    Component Label Display Value Save Input To Required
    Text Expense Item ri!expenseItem ri!expenseItem selected
    Date Expense Date ri!expenseDate ri!expenseDate selected
    Decimal Expense Amount ri!expenseAmount ri!expenseAmount selected

    For example:

    images/sail_tutorial/

  3. Click SAVE CHANGES.

Your interface should now look like this:

images/sail_tutorial/

Conditionally show a component

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

To configure the visibility of the Comments field:

  1. In the RULE INPUTS pane, click New Rule Input .
  2. Configure the following properties:
    • For Name, enter: comments.
    • For Type, select Text.
    • For Array, leave blank.
  3. Click CREATE.
  4. Drag a PARAGRAPH component from the PALETTE panel to the form, and place it beneath the Expense Amount component.

    images/sail_tutorial/

  5. In the COMPONENT CONFIGURATION pane, configure the following properties:

    Property Description
    Label Comments
    Display Value and Save Input To ri!comments
    Required selected
  6. Under Visibility, select Only show when ….
  7. Click Edit Condition.

    images/sail_tutorial/

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

    images/sail_tutorial/

  9. Click OK to close the expression box.

    When you click this box, Appian immediately applies the conditional expression, so your Comments component will disappear. To see the component, go to the next step to test the expression, or follow the steps for selecting the hidden component.

  10. Test the conditional expression by entering 101 in the Expense Amount field, then clicking out of the field, or pressing the Enter key to see the paragraph component displayed.

images/sail_tutorial/

  1. Click SAVE CHANGES.

Select a hidden component

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

When you click on the component in the expanded panel, the COMPONENT CONFIGURATION pane populates with the properties for that component, and you can change the properties as needed.

Add a validation

You can 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 add a custom validation:

  1. Select the Comments paragraph component.
  2. In the COMPONENT CONFIGURATION pane, hover over the Validations property, then click the Edit as Expression icon.

    images/sail_tutorial/

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

    1
    
    if(len(ri!comments)<=100, null, "Your text has exceeded 100 characters")
    
  4. Click OK to close the expression box.
  5. Test the validation:
    • For Expense Amount, enter: 101
    • For Comments, enter more than 100 characters, then click out of the field. You should see your validation message.

    SAIL_Tutorial_Comment_Validation_Message

  6. Click SAVE CHANGES.

Add a reusable component

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 that component on every interface, we can move the paragraph component into a new interface object and simply call the interface object whenever we need the paragraph component.

View the Comments field expression

First, we'll want to take a look at the expression that represents the Comments field in the AT_expenseReportForm interface.

To view the expression:

  1. Select the Comments paragraph component.
  2. In the COMPONENT CONFIGURATION pane, click the Edit as Expression button in the Paragraph section header.

    images/sail_tutorial/

    You should now see the expression for the interface.

    SAIL_Tutorial_Edit_Icon_Header_Expression

    The properties listed in this expression correspond to the properties you set in the COMPONENTS CONFIGURATION pane.

    Typically, at this point, you'd want to copy this expression so that you can paste it into a new interface, where you then edit the expression to make it reusable. For this tutorial, we're going to save you that copying and editing step by including the edited version of the expression in the next step, so no need to copy the expression here.

Create the reusable interface

Next, let's create the reusable interface and configure it using the expression from the original interface.

To create and configure the reusable interface:

  1. In the application view, click New > Interface.
  2. Name the interface AT_enterComments, and save it in the AT Rules & Constants folder.
  3. Click CREATE. The new interface opens in the EDIT view.
  4. Drag a PARAGRAPH component from the PALETTE panel, and drop it onto the EDIT view.
  5. In the RULE INPUTS pane, add the following rule inputs:

    Name Type Array
    showWhen Boolean leave unselected
    labelPosition Text leave unselected
    readOnly Boolean leave unselected
    required Boolean leave unselected
    commentValue Text leave unselected
    commentSaveInto Text selected
  6. Select the Paragraph component if it's not already selected.
  7. In the COMPONENT CONFIGURATION pane, click the Edit as Expression button in the Paragraph section header.
  8. Replace the current expression with the following expression:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    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")
    )
    

    After you paste, remove the plus sign (+) at the start of the readOnly property line. This character is present only to indicate that the readOnly property was added to the expression.

    This expression represents the Comments expression from the original interface, but has been edited so that it can be reused. The edited properties are highlighted for your reference.

    With these changes, the Comments component can receive values from the parent interface, so the comments can be handled in ways specific to that interface. In this example, the Comments data could be saved to a data entity for expense report data, as set in the parent interface. This same reusable interface could also be used in a time-off request form, and the Comments data would be saved to the data entity for time-off requests instead, based on the properties in the parent interface.

  9. Click OK to close the expression editor.
  10. Click SAVE CHANGES.
  11. Close the reusable interface.

Call the reusable interface

Now that we've created the reusable interface, we can call it in our original interface.

To call the reusable interface:

  1. Return to the AT_expenseReportForm interface.
  2. Select the Comments component.
  3. In the COMPONENT CONFIGURATION pane, click the Edit as Expression button in the Paragraph section header.
  4. Replace the existing expression with the following:

    1
    2
    3
    4
    5
    6
    7
    8
    
    rule!AT_enterComments(
      showWhen: ri!expenseAmount > 100,
      labelPosition: "ABOVE",
      readOnly: false,
      required: true,
      commentValue: ri!comments,
      commentSaveInto: ri!comments
    )
    
  5. Click OK.
  6. Test the new expression by entering 101 for Expense Amount, then hovering over the Comments field when it appears. You should see the following:

    images/sail_tutorial/

    Note that your Comments field is now calling AT_enterComments.

  7. Click SAVE CHANGES.

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.

We have a form to submit an expense report, and now we need a form that an approver can use to review the expense report. The only difference between these forms is that the review form will need to be read-only and contain an additional input so the managers can approve or reject the expense report.

This means we don't have to design two interfaces. Instead, we can configure the components of our single AT_expenseReportForm interface to display as read-only under certain conditions.

To make this interface conditionally read-only, we'll:

  1. Add new rule inputs to the interface.
  2. Add review options to the interface.
  3. Add local variables to the interface.
  4. Configure the input fields.
  5. Configure the review options.
  6. Hide the cancel button.
  7. Make the form title dynamic.
  8. Save your changes.
  9. Test the read-only interface.

Add new rule inputs

Add the following rule inputs to the AT_expenseReportForm interface:

Name Type Array
step Text leave unselected
approve Boolean leave unselected

Add review options

To add review options to the interface:

  1. Drag a RADIO BUTTONS component from the PALETTE panel to the form, and place it beneath the Comments field.
  2. Select the Radio Buttons component if it's not already selected.
  3. In the COMPONENT CONFIGURATION pane, set the following properties:
    • Label Position: HIDDEN
    • Selected Value: ri!approve
    • Save Selection To: ri!approve
    • Required: selected
  4. Under Choice Labels, click List of Text.
    • Enter Approve for the first option.
    • Enter Reject for the second option.
    • Click the back link in the Radio Buttons section header.

      images/sail_tutorial/

  5. 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.
  6. Click SAVE CHANGES.

Add local variables

Local variables allow you to store interface-specific values that can be used to affect the behavior of the interface.

For example, in this case, we're going to add a local variable (local!readOnly) that tells the interface when it should be considered read-only. The interface should be read-only if we're in the review phase, so we'll define this variable based on whether the ri!step value is REVIEW.

In this tutorial, we'll use the TEST feature to set the ri!step value to REVIEW. In a real-world scenario, the process calling this interface would pass the ri!step value to the interface.

We'll also add a local variable (local!labelPosition) that specifies the label position for components when the interface is read-only. It saves us time to add this value here, rather than as a property in each individual interface component.

We need to know the values of these variables as soon as we open the interface, so we'll put the entire interface definition into the a!localVariables() function. As a result, the local variables will initialize when the interface loads.

To add local variables to the interface:

  1. Click </> EXPRESSION MODE in the title bar. This action replaces the component palette with the INTERFACE DEFINITION panel, where you can see the entire expression that defines the interface.

    SAIL_Tutorial_Expression_View_Button.png

  2. Edit the interface definition so that the properties match the values highlighted below:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    
    +    a!localVariables(
    +      local!readOnly: ri!step="REVIEW",
    +      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!AT_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
                )
              }
            )
          )
    + )
    
  3. Click SAVE CHANGES.

Configure the input fields

The interface currently has the following input fields:

  • The Expense Item field (a!textField)
  • The Expense Date field (a!dateField)
  • The Expense Amount field (a!floatingPointField)
  • The Comments field (rule!AT_enterComments),

We want to configure these fields so that they are conditionally read-only and optional when the interface is in read-only mode. We'll use the new local!readOnly variable to do this.

We also want to change the position of each field's label based on whether the interface is in read-only mode. We'll use the new local!labelPosition to do this.

For each input field listed above, make the following changes:

  • Set the labelPosition property to local!labelPosition.
  • Set the required property to not(local!readOnly).
  • Add a readOnly property set to local!readOnly.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
a!localVariables(
  local!readOnly: ri!step="REVIEW",
  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!AT_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
        )
      }
    )
  )
)

Configure the review options

We want the review options to display only when the interface is on the review step of the workflow.

For the review options component (a!radioButtonField), add a showWhen property set to local!readOnly.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
a!localVariables(
  local!readOnly: ri!step="REVIEW",
  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!AT_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
        )
      }
    )
  )
)

Hide the cancel button

We want to hide the CANCEL button on the review step.

For the appropriate a!buttonWidget component, add a showWhen property set to not(local!readOnly).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
a!localVariables(
  local!readOnly: ri!step="REVIEW",
  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!AT_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)
        )
      }
    )
  )
)

Make the form title dynamic

We want the form title to change based on which step in the workflow we're on.

For the a!formLayout component, set the label property to the following expression:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
a!localVariables(
  local!readOnly: ri!step="REVIEW",
  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!AT_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)
        )
      }
    )
  )
)

Save your changes

Click SAVE CHANGES.

Test the read-only interface

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

To test this behavior:

  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): 08/03/2021
    • expenseAmount (Number (Decimal)): 120.87
    • comments (Text): Team lunch for 6 people.
    • step (Text): REVIEW

      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 did it! You created an interface that satisfies two business use cases. You're ready to move on to other tutorials.

See Also: Interface Recipes to create different interfaces with specific layouts and dynamic behavior

Open in Github Built: Fri, Dec 03, 2021 (03:08:11 PM)

On This Page

FEEDBACK