Service Catalog
enables you to create service forms quickly and easily. Following are some
general guidelines and principles for designing optimal service forms.
- JavaScript
code
should
be
specific
to
a
dictionary. Ensure each function in the code is
stand-alone and does not depend on any other function or dictionary. This is to
make sure the code functions even if a new dictionary is added or existing
dictionaries are removed. For example, assume that two dictionaries used in a
particular service have code that must be executed in an onLoad event. Rather
than writing one monolithic function, write two dictionary-specific functions,
place them in a library, and call them from a wrapper function, which is
defined as a Script and attached as the onLoad event to the form:
Common_Service_onLoad () { IT_Dictionary1_onLoad(); IT_Dictionary2_onLoad();}
- Create
service-independent
code. Testing the code in one service suffices for
all services in which that code is used. The code in the previous example is
not service-independent. It would fail if the Common_Service_onLoad() function
were executed in a service that was missing one or both of the dictionaries.
However, this can easily be modified:
Common_Service_onLoad () { if (serviceForm.ITDictionary1 !- undefined) { IT_Dictionary1_onLoad(); } if (serviceForm.ITDictionary2 !- undefined) { IT_Dictionary2_onLoad();}
Just as the above
code tests for the presence of a dictionary in a service before attempting to
apply dictionary-specific code, you may need to test for the presence of a
particular field before attempting to apply field-specific code. It is best
practice to use the dictionary in only one Active Form Component; however,
service-specific rules may affect the dictionary's appearance. For example,
displaying the supervisor information for the person requesting a service may
only be required for those services that require supervisor approval.
Therefore, code that attempts to manipulate the supervisor-related fields
should be included in a code block such as:
FirstApprover_onLoad () { if (serviceForm.FirstApprover.SupervisorName !- undefined) {// code goes here; }}
A field may be used
in a form, but conditionally hidden by a rule or ISF code previously executed.
In cases like these, code like the following might be more appropriate, and
more robust:
if
(serviceForm.FirstApprover.SupervisorName !- undefined) { if
(serviceForm.FirstApprover.SupervisorName.isVisible()) {// code goes here; }
- Send
less
data
to
the
browser
session: To improve the performance of the form
when it is loaded, less data should be sent to the browser session. In previous
releases of Service Catalog, any dictionary fields you needed to manipulate had
to be sent to the browser in order to be manipulated. Using server-side events,
you can manipulate data in dictionaries such that less data is sent to the
browser and also it keeps sensitive data under the control of server-side
execution and therefore incapable of being intercepted. See
Server-Side Data Retrieval Rule.
For sending less data, you can do the following:
- Group fields such that
fields that do not need to be loaded into the browser session—at least not
during the Ordering moment—should be grouped together into one or more
“nonloaded” dictionaries. A common example of a “nonloaded” dictionary is one
supplying the parameters to an orchestration Agent. The orchestrator often
needs more data than the user ever sees on the form. This data is typically
derived through conditional rules or data retrieval rules that determine who
the user is, what role he has, what OU membership he has, and so on; and all of
this is best derived either before the form is loaded in the browser (during
the pre-Load event) or once the user has clicked the Submit button (during the
post-Submit event).
Seeing the effects of
rules operating on “nonloaded” dictionaries will take a bit more testing, since
you won’t be able to see the values being set until the post-Submit event has
occurred. Use of “nonloaded” dictionaries will therefore typically involve
checking the form in a subsequent moment (for example, Service Group
Authorization or Service Delivery), as a user who has the Manage Service
Dictionaries permission and is therefore able to see all the dictionaries
defined on the form.
- Use
the
pre-Load
event
rather
than
the
on-Load
event. Generally, conditional rules and data
retrieval rules take action on objects that are present in the browser. So a
“Hide fields” conditional rule that is tied to the pre-Load event, has in fact
nothing to hide, because the fields being hidden do not actually exist yet
during the pre-Load event. There is a notable exception to this general rule,
as explained in the previous discussion of “nonloaded” dictionaries.
It is possible to
manipulate the values of fields that are not loaded into the browser, through
either the Set Value conditional rule action, or a data retrieval rule.
The conditional rule
and data retrieval rule functionality is highly flexible, enabling you to
combine multiple rule actions on multiple targets, and to tie any one rule to
multiple events. Any actions that do not have access to their targets—for
example, a Set Value action on a field that is not actually present on the
service form—are said to “fail silently” or take no effect. If you have a
conditional rule that performs a sequence of actions (say, hiding some fields,
making others mandatory, and popping up an Alert message; as well as executing
a Set Value), you may see most of those actions taking effect in the browser.
The effect of a Set Value action, however, may not be visible because either
the target field is currently hidden or it is not present in the browser. The
conditional rule framework does not stop you from constructing such a rule; it
merely ignores any actions that cannot be executed.
The conditional rule
and data retrieval rule wizards spell out these behaviors as you construct the
rules. Help text embedded into each step explains any limitations you may
experience when using certain actions on the server-side events.
- Server-side events provide
you with an opportunity to write data directly to the database—specifically, to
the WDDX data stored in the database for each requisition entry. The
post-Submit event in particular is your window of opportunity for writing the
data that was collected or derived from actions in the browser into the
database.
An important
distinction between the pre-Load and post-Submit events is that pre-Load can
write to the database only if the requisition data is already in the database.
In other words, if the user has not yet clicked the Order or Add & Review
buttons, the data for the requisition does not exist in the database and the
pre-Load rules have nothing to manipulate. While the pre-Load event gives you
the ability to change the viewable value, it does not persist that value in the
database. The post-Submit event provides the persistence.
- Server-Side events provides
you with great flexibility for manipulating data that is part of the service
form yet protected by the server. What makes this possible is that the
“nonloaded” dictionaries are stored in the database and therefore accessible by
server-side rules. Although the browser-side rules are generated as JavaScript
on the form, the server-side equivalents of those rules are actually generated
as Java code—so there are, in effect, two separate manifestations of rules
possible. The ISF framework (documented in the
Interactive Service Forms (ISF) API Overview)
enables you to write JavaScript for more complex manipulations of the form data
and appearance. This JavaScript can only execute in the browser. Therefore any
ISF functions you write cannot be tied to the server-side events.
- This
Editable
on
server-side
only option, available on the HTML Representation
tab for any field defined as Input Type = read-only or hidden, helps you
satisfy what are sometimes conflicting requirements: the first is to display
helpful data to the requesting user or use “hidden variables” on the form that
drive business logic; and the second is to secure data completely from user
intervention (including malicious activity).
Two common use-cases
for using this flag are:
-
- The person-based dictionary,
a “template dictionary” you can create in the Dictionaries module
- The service-item-based
dictionary, the structure of which is also automatically created in the
Dictionaries module
The two are similar
in that they can both exhibit “autopopulation” behavior. In the person-based
dictionary, the form user chooses a person (using the Select Person control)
and that person’s details are automatically displayed on the form. In the
service-item-based dictionary, the form user can choose a particular service
item he owns or has access to, and the attribute values for that service item
instance are automatically displayed.
It is a common
service design technique to define most (if not all) of the attributes
automatically populated as read-only fields (that is, configured with an Input
Type on the HTML representation tab as “read-only”). For example, if your user
base is large enough to contain multiple people with the same name, you may use
attributes such as the email address to help the form user differentiate among
those people and choose the right person. Of course you would not expect the
form user to be updating the chosen user’s email address. On the other hand,
the form user may know this person well and therefore know that he uses his
mobile phone exclusively and not the desk phone number obtained through
Directory Integration. If this is the case, you may want to make the Work Phone
field editable and not just read-only, so that at least the service delivery
personnel handling the request have the most reliable way of reaching the
chosen person.
Using the “Editable
on server-side only” check box ensures that any fields you mark as Input Type =
read-only or hidden are entirely controlled by the server and that any
manipulation of their values in the browser session is discarded in favor of
the data residing in the Service Catalog database. In fact the only mechanism
to override the data residing in the database is a rule (either conditional or
data retrieval) that executes during a server-side event, and in this case that
rule is actually updating the database value. (In the case of the person-based
dictionary, the Person record in DirPerson is not updated, but the WDDX data
stored in the database for the requisition entry is.)
In other words,
you—the service designer—can manipulate the value of a field marked as
“Editable on server-side only,” but you can do so only through rules executing
during server-side events. This is an important factor to consider, because you
may be tempted to think that all you have to do to use these “protected” fields
is to check the “Editable on server-side only” flag. That is all that’s
required if you do not have any reason to manipulate the value returned from
the database. But if your business logic requires that you set these values in
response to selections the form user makes, you must be sure to tie any
conditional rule (for a Set Value action) or data retrieval rule (for a data
distribution from a query) to a server-side event.
- Naming
Conventions: Although it is perfectly legitimate,
you should not give a dictionary, form, and field the same name. It results in
a very confusing display. Dictionaries should ideally use a prefix notation to
denote their usage and perhaps the dictionary group in which they have been
placed. For example, a “Reason” dictionary sounds like it is fairly generic,
used in many forms. Therefore, it makes sense to place it in a dictionary group
named, “Common”, and to prefix the dictionary name with a code designating this
dictionary group, for example, CMN_Reason. Dictionary groups could reflect the
service group in which a service-specific dictionary is used, or perhaps the
company department or division for which the dictionary and service have been
developed.
An easily understood
naming convention for forms is also needed. This is especially critical if you
need to differentiate between multiple forms that include the same dictionary
or group of dictionaries.
- Form-Dictionary
Relationship: One form should have only one
dictionary. Multiple dictionaries should be included in the same form if:
- These
dictionaries will all be required in the same services.
- The order in
which the dictionaries are presented is the same in all services.
- No additional
dictionaries need to be displayed between the dictionaries in this form. On a
service form, all dictionaries in one form component are displayed, in the
order specified in that component; then, dictionaries in the next form
component included in the service are displayed, in the order specified, and so
on.
Examples:
- Assume that a
group of services developed for the Facilities department have a chain of
approvals consisting of up to three approvers, based on the customer's position
or department. In this case the Facilities_Approval form should include three
dictionaries: FirstApprover, SecondApprover, and ThirdApprover, as well as
rules to determine how many of the three approvals actually need to be
collected.
- Assume that most
of the services developed for the Facilities department require the standard
up-to-three approvers, but a rather expensive service requires an additional
approver, at the VP level. You have two options:
One Form
|
Modify the
Facilities_Approval form so that it includes the VP Approver dictionary in
addition to the FirstApprover, SecondApprover, and ThirdApprover, and write an
additional rule or rules to display and process the VPApprover dictionary only
for that one service.
|
Multiple
Forms
|
Create
another form, Facilities_VP_Approval, which includes the VPApprover dictionary
and duplicates the HTML, access control, and rules needed to process the
FirstApprover, SecondApprover, and ThirdApprover dictionaries.
|
In most cases, the
first solution (one slightly more complex form) should be preferred. You don't
have to duplicate work to configure three dictionaries and their associated
rules in more than one form. And, if any of those dictionaries or their rules
needs to be changed, there is only one place to change them. You should only
consider the second solution (two partly redundant forms) when including the
additional dictionary would also cause extensive changes to the way the other
dictionaries are displayed or processed.
- Different
Renderings
for
the
Same
Dictionary: What if the form's behavior or
appearance needs to vary greatly, based on the services in which the form is
used? Various scenarios are available. Only you, the service designer, can
decide which scenario is preferable, based on the criteria outlined below. For
example, virtually every service needs to include that “Reason” dictionary. But
sometimes the field label should read “Reason”, sometimes “Justification”,
sometimes “Explanation”. Further, the help text associated with the field needs
to be substantially different for each service or group of services. In this
case, since the dictionary is simple and easily configured, a decision is easy:
Create a separate form for each rendering of the dictionary, and include the
appropriate form in the corresponding services.
But what if the
field or fields that need to be customized on a service-by-service basis were
part of a large dictionary with potentially complex rules? You still have the
same two options outlined above: one form or multiple forms. However:
One Form
|
Create
one form and customize its appearance using ISF functions.
|
Multiple
Forms
|
Create a
different form for each rendering of the dictionary.
|
The One-Form option
is now complicated by the fact that you cannot customize the dictionary's
appearance using conditional rules—conditional rules only modify a field's
appearance and behavior, not the contents of the field's label and help text.
The Multiple-Forms option has the same drawbacks as before—you need to do the
work upfront to create the forms, and the maintenance work to maintain rules in
multiple places.
There are two
additional options:
-
- Get the
requirements changed. This is not likely nor a good idea, if it results in the
users getting less information about how to supply information in requesting a
service.
- Put the
problematic field in a separate dictionary that is used in multiple forms, and
keep one form for the rest of the dictionary. This is only possible if the
field can be displayed before or after all the other fields, not in the middle.
- Dictionaries,
Forms,
Rules
and
Services: A form typically consists of a set of
dictionaries and a set of rules.
- The rules
may affect not only those dictionaries (and their fields) in the same form but
dictionaries in other forms.
- It is very
important to test the rules thoroughly in the context of a particular service,
to ensure that all referenced dictionaries are in forms included in that
service. The Best Practices Reports include a “Dictionaries in Services” report
that will facilitate doing this testing and doing an impact analysis to assess
potential effects of any proposed changes to dictionaries or rules.
It is also
perfectly reasonable to structure Form 2 so that it contains only rules to be
applied to a particular service. Many services could include only Form 1, but
only the service with extra requirements includes Form 2. This greatly
simplified (or potentially eliminates) conditions that would be needed in the
rules in Form 2, to ensure they only fire in the appropriate services.
This also
eliminates having to write a conditional rule that tests for the service name—a
textual element that is subject to change. You would merely have to include the
rules-only form in the service affected, sequencing it after the form whose
dictionaries are affected. Rules are executed in the order in which the forms
are included in the service, and then in the order in which the rules are
arranged within the form.
- Loosely
Coupled
vs.
Tightly
Coupled
Rules
and
Dictionaries: When rules and the dictionaries they
affect and refer to are in the same AFC, the rules and dictionaries are said to
be “tightly coupled”. The example above, with a rule affecting the appearance
of a dictionary in another form, shows “loosely coupled” rules and
dictionaries. In general, a rule can affect a dictionary in any form, provided
both forms are included in the same service. There is, however, one significant
limitation on loosely coupling rules and dictionaries—a rule defined in one
form cannot be triggered by an field-level event attached to a field in a
dictionary in another form. Therefore, loosely coupled rules typically need to
be triggered by a form-level event, when the form is loaded or submitted.
- Rules,
ISF
and
the
Requisition
Life
Cycle: When a request is submitted, all of the
definitions for the dictionaries, HTML representation, access control, and task
plan for that service are stored (in a compressed format) with the request
data. This ensures that a request can be processed throughout the authorization
and delivery moments without any subsequent changes to the form's definition
affecting the behavior or appearance of the service form for the in-flight
request.
If you change a
dictionary, all forms using that dictionary automatically inherit the change
and all services which use those forms also inherit the change. Any request for
one of the changed services that has been saved but not submitted are marked as
obsolete. The user will need to cancel the request or remove the service, read
it, and fill in the service form data. Submitted requests are not affected by
changes to dictionaries or active form components.
However, rules and
ISF functions (whether in a Script or an external library) are always loaded
dynamically when a service form is displayed in My Services or Service Manager.
Therefore, you must ensure that a current version of the rule/code does not
refer to a field no longer on the form or, conversely, that a field on previous
versions of the form, but not longer used, can have an associated function.
This flexibility is easily accomplished by always checking for the existence of
a dictionary or field before attempting to manipulate it via ISF, as discussed
previously. If desired, a different version of a library could be attached to
the newer versions of the form so that, for example, the names of functions
would not need to change, but their contents could be updated to comply with
revised requirements.
- Changing
a
Dictionary
or
Active
Form
Component: If you change the definition of a field
within a dictionary, all active form components will reflect that change.
Similarly, if you change any aspect of the active form component's definition,
all service definitions that use that form will reflect that change. You cannot
delete/change a dictionary or dictionary field to which a conditional rule is
associated, or that triggers a data retrieval rule; you must remove the
associations first. This check is not performed for JavaScript functions. Each
time the form is changed, the application automatically updates the version
number for all service definitions which incorporate that form. You may see a
brief delay in saving such changes if the form is used within many services.
This may also affect any Requisition API (RAPI) programs implemented for
ordering the service, since the RAPI SubmitRequest operations requires the
version number of the service to be specified.
- Coding
SQL
Entry
Data
Retrieval
Rules: If you are not familiar with SQL, it may be
a bit intimidating to code a SQL Entry data retrieval rule. A “trick” to help
familiarize yourself with SQL is to start by configuring a simpler version of
the rule as a Database Table Lookup. After you have saved the rule, the rule
summary displays the generated SQL. You can copy the SQL statement, paste it
into a SQL Entry data retrieval rule, and modify it as required.
- Using Customer and Initiator Lightweight
Namespaces: The #Customer# and #Initiator# lightweight namespaces
are automatically supplied as the default values for fields in the
Customer_Information and Initiator_Information dictionaries used in the
Customer-Initiator form component. However, these namespaces could be used as
default values for any form component.
The design of
dictionaries to hold information about customer location could use this
technique. Fields about the customer’s location could be included in the
Customer_Information dictionary. However, such fields may be required on very
few services—only those where a service must be delivered to the customer’s
physical location. A more flexible design may be to configure a separate
dictionary (and form component) for the customer location, and include this
form component only in services where customer location is relevant.
Multiple namespaces
can be specified as a default value. For example, the expression
#Customer.FirstName# #Customer.LastName# concatenates the customer’s first name
and last name, separated by one space. This expression duplicates the
appearance of the first field of a person-based dictionary.
Note |
The use of grid
dictionary fields for lightweight namespaces is not currently supported.
|