Build a Wizard with Milestone Navigation

Interface patterns give you an opportunity to explore different interface designs. Be sure to check out How to Adapt a Pattern for Your Application.

Goal

Use the milestone component to show steps in a wizard.

This design pattern is not recommended for offline interfaces because reflecting immediate changes in an interface based on user interaction requires a connection to the server.

Configure links for each step in the milestone so that the user can move forward or return to any step.

This scenario demonstrates:

  • How to create a wizard using the showWhen parameter of an interface component
  • How to use a milestone field to show the current step of the wizard
  • How to show and change the style of buttons

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
a!localVariables(
  local!employee:{ firstName:null, lastName:null, department:null, title:null, phoneNumber:null, startDate:null },
  local!currentStep: 1,
  local!steps: {"Step 1", "Step 2", "Review"},
  a!formLayout(
    label: "Example: Onboarding Wizard",
    contents:{
      a!sectionLayout(
        contents:{
          a!milestoneField(
            steps: local!steps,
            active: local!currentStep
          )
        }
      ),
      a!sectionLayout(
        contents:{
          a!columnsLayout(
            columns:{
              a!columnLayout(
                contents:{
                  a!textField(
                    label: "First Name",
                    labelPosition: if( local!currentStep = 3, "ADJACENT","ABOVE"),
                    value: local!employee.firstName,
                    saveInto: local!employee.firstName,
                    readOnly: local!currentStep = 3,
                    required: not(local!currentStep = 3),
                    showWhen: or( local!currentStep = {1,3} )
                  ),
                  a!textField(
                    label: "Last Name",
                    labelPosition: if( local!currentStep = 3, "ADJACENT","ABOVE"),
                    value: local!employee.lastName,
                    saveInto: local!employee.lastName,
                    readOnly: local!currentStep = 3,
                    required: not(local!currentStep = 3),
                    showWhen: or( local!currentStep = {1,3} )
                  ),
                  a!textField(
                    label: "Phone Number",
                    labelPosition: if( local!currentStep = 3, "ADJACENT","ABOVE"),
                    value: local!employee.phoneNumber,
                    saveInto: local!employee.phoneNumber,
                    readOnly: local!currentStep = 3,
                    required: not(local!currentStep = 3),
                    showWhen: or( local!currentStep = {2,3} )
                  )
                }
              ),
              a!columnLayout(
                contents:{
                  a!textField(
                    label: "Department",
                    labelPosition: if( local!currentStep = 3, "ADJACENT","ABOVE"),
                    value: local!employee.department,
                    saveInto: local!employee.department,
                    readOnly: local!currentStep = 3,
                    required: not(local!currentStep = 3),
                    showWhen: or( local!currentStep = {1,3} )
                  ),
                  a!textField(
                    label: "Title",
                    labelPosition: if( local!currentStep = 3, "ADJACENT","ABOVE"),
                    value: local!employee.title,
                    saveInto: local!employee.title,
                    readOnly: local!currentStep = 3,
                    required: not(local!currentStep = 3),
                    showWhen: or( local!currentStep = {1,3} )
                  ),
                  a!dateField(
                    label: "Start Date",
                    labelPosition: if( local!currentStep = 3, "ADJACENT","ABOVE"),
                    value: local!employee.startDate,
                    saveInto: local!employee.startDate,
                    readOnly: local!currentStep = 3,
                    required: not(local!currentStep = 3),
                    showWhen: or( local!currentStep = {2,3} )
                  )
                }
              )
            }
          )
        }
      )
    },
    buttons: a!buttonLayout(
      primaryButtons: {
        a!buttonWidget(
          label: "Go Back",
          value: local!currentStep - 1,
          saveInto: local!currentStep,
          showWhen: or( local!currentStep = {2,3} )
        ),
        a!buttonWidget(
          label: if( local!currentStep = 1, "Next", "Go to Review"),
          value: local!currentStep + 1,
          saveInto: local!currentStep,
          validate: true,
          showWhen: or( local!currentStep = {1,2} )
        ),
        a!buttonWidget(
          label: "Onboard Employee",
          style: "PRIMARY",
          submit: true,
          showWhen: local!currentStep = 3
        )
      }
    )
  )
)

Test it out

  1. Click Next without entering a value in any of the fields. The user will stay on step 1 until all fields are entered.
  2. Enter values for all the field. Click Next. Notice the milestone progress will move to step 2
  3. Enter values for all required fields in step 2. Click Go to Review. Notice that all values entered will appear as a set of read only data and the milestone field progresses to 'Review'

Notable Implementation Details

  • The showWhen parameter is used extensively to conditionally show particular fields as well as set requiredness & read only setting.
  • Each "Next" button is configured to increment local!currentStep. Validate is set to true, which ensures all fields are required before continuing on to the next step.
  • Each "Go Back" button is not configured to enforce validation. This allows the user to go to a previous step even if the current step has null required fields and other invalid fields. This button also decrements local!currentStep by one.
  • The milestone field is configured without links, however links could be added that navigate a user back to a particular step.
FEEDBACK