Developing Process Management forms
You can develop NexJ Process Management forms using Scheme or JavaScript. For external systems, you can use REST APIs to retrieve forms data from NexJ CRM and update forms data in NexJ CRM.
For information about using REST APIs to develop forms for external systems, see Developing forms using REST APIs.
Forms are stored as libraries in the Resources layer of your model. Within the library, each form is defined as a flow. Each form (or flow) should be defined in its own library, with each flow library using the following naming convention:
Scheme
bp:register-flow <formname> #t
(bp:flow ...)
)
JavaScript
bp.registerFlow(<formname>, true,
(function() {
return bp.flow(...);
})()
);
Where <formname>
is a unique identifier for the form, and composed of all lowercase letters. For example, simple
.
Simple forms
A simple form collects information in a single page and single section. A simple form can bind to a single class in the underlying business model. This means that you can use the attributes of the underlying model to provide form values, calculations and validation.
Depending on the process using the form, you can save form information and use it to update underlying model information.
Creating a simple form
When creating a simple form, assume that you are creating a flow to capture a contact's basic information in your system.
The following example builds a simple form. It creates a page with an ID of Client_Details, captioned with the title Client Details. This page contains a single section labeled Profile. The fields on the page capture the following client information:
- First name
- Initials
- Last Name
- Full Name
- Image of the client
- Birthdate
- Gender
- Service tier
- Notes
When you create a form, define a question for each of these client attributes. These questions become fields on the form. Each question has the following mandatory parts:
- Type of question - Affects the control used to obtain the answer.
ID of the question (optional) - If this ID matches an attribute in the class that the form section binds to, the value of that attribute becomes the initial answer to that question on the form. If you want to refer to the question's answer from elsewhere in the form, you must give it an ID.
Questions of type "label" do not have IDs.
For example:
The following image shows an example of a simple form. The labels on the form come from the caption values of the Person
class that the form binds to. Indication that a field is mandatory also comes from the model and reflects whether the attribute's Required
property is set.
Adding field captions and ghost text
If the attribute caption taken from the model is not sufficient or unclear in the context of your form, you can change what is displayed by adding a caption statement:
Scheme
(: caption "caption_text")
JavaScript
caption: "Caption text",
You can also use this to provide labels for questions that do not bind to a model attribute. Caption text appears on the line of the entry field.
Additionally, for text fields, you can provide instructional text by adding a hint statement:
Scheme
(: hint "hint_text")
JavaScript
hint: "hint text"
Text in this statement appears underneath the entry field.
For example, the following code creates a form that gets first and last name information from the Person
class, but overrides the captions with Given name and Family name. It also includes a date field that has no mapping to the underlying model and the caption for this question comes directly from the form definition.
Mandatory and optional fields
By default, answers to questions are optional. The form's end-user does not have to provide a value for every question field on the form. In a bound field, if the underlying attribute is marked as required, an answer to that question is also mandatory. If you want to mark other questions as mandatory, use the following attribute statement:
Scheme
(: required #t)
JavaScript
required: true
Using the previous example, if you want to make the Due date field mandatory, code the following:
Mandatory fields are validated immediately on edit. There is no need to validate again when saving. To validate all fields without any edits, click Submit.
Setting initial values
You can set an initial value for a field using the following option:
Scheme
(: initialize "value")
JavaScript
{initialize: "value"}
For example, to set the default value of tier to A, specify the following:
Remember to validate that the field allows the initialization value.
If a value for a given question already exists in the underlying model, that value becomes the initial value regardless of what is set by Initialize.
Setting calculated values
In some cases, you may want to display a calculated field to either summarize information from elsewhere in the form, or retrieve information to reference from the system data. Calculated fields are defined using the following option:
Scheme
(: value (string-append <value1> <value2>))
JavaScript
value: <value1> + <value2>
For example, the following code snippet takes the values of the firstName
, middleName
, and lastName
fields, concatenates them, and displays them as a fullName
value.
To concatenate two data types into a string when creating a caption using Scheme, add a check for values as shown in the following code in order to prevent problems caused by undefined values. In the following example, assetValue
is a number and asset
is a string.
(: captions
(if (null? (@ question assetValue))
(@ question asset)
; else
(string-affix (@ question asset) " " (number->string (@ question assetvalue)))
)
)
Multi-step and multi-section forms
Defining a multi-step form
All forms appear on a single screen. Multi-step forms organize related questions together into expandable steps on a single screen. You are always able to navigate to steps within a form to review and change answers.
You can also use multi-step forms to control the flow of information gathering by making the appearance of later steps on a form dependent on answers provided earlier.
The following code defines a simple two-step form. The resulting form is also included.
Note the following:
- Each step is defined in the code as a "page".
- You can provide a caption for every step to help users navigate longer forms. Step (page) captions are always visible.
- You can provide section captions, which provide guidance within a given step. Section captions are only visible when a step is open.
- You can provide a number for each step, which can be up to two characters, and which appears to the side of the form navigation.
- Step and section captions are not required, but are recommended to help users navigate the form.
Page 1
|
Defining a multi-section form
You can additionally divide each step of the form into multiple sections. Like steps, sections help you to group related questions together visually to help with navigating and filling out the form. Unlike steps, however, sections do not expand and collapse as you move between them. The following example code displays the same information as for the multi-step example, but groups it into two sections instead of two steps.
Note the following:
- Each section within a step displays as a separate card.
- All of the sections within the step, display at the same time. You cannot expand and collapse sections.
- If you plan to bind your form to the underlying data, sections can help you access information stored in associated class attributes. For more information on this, see Binding forms and navigating attributes.
You may want to allow a section or step to repeat multiple times in order to collect data sets of varying sizes. To accomplish this, add the following attribute to the form element you want to repeat.
Scheme
(: collection #t)
JavaScript
collection: true
This is usually done on a section-by-section basis, but entire steps can also be defined as collections.
Collections of sections
For example, assume you are creating a form in which part of the information you want to gather is a list of major assets belonging to a client.
For each entry, include a text field with a description of the asset, and another with the estimated value. For example:
When this section first opens, it looks like:
When you click the Add icon , the first entry opens:
After filling out a couple of entries, the form looks like this:
You can collapse each section without losing data:
Collection of steps
In most cases, sections are used to create collections. For comparison, the following code defines the collection at the step level:
In this example, the initial state seen by the user is:
The user filling out the form can click Save and Add Another to create each entry in the collection. When completed as in the previous example, it looks like this:
Only one step in the collection can be open at any time. This is consistent with the general step behavior.
Forms with conditions and validation
You can add conditions to forms to control the behavior of fields, sections, and pages. The two primary uses of conditions are to control visibility and requirement, that is, whether questions, sections, or steps are visible to users, and whether questions are mandatory.
In determining these conditions, you can make use of any number of the following:
- Answers given to preceding questions in the form.
- Values from the underlying data that could be included as questions in the form.
- Operators such as
exists, equals, starts with,
orcontains.
Referencing other questions
When you add a condition that is based on the value provided by another question, you must explicitly reference that question in your condition. The referenced question, therefore, must have an ID in its definition. A question's conditions can reference any other question in the same section, or any question in an earlier section or step.
The syntax for making these references:
For referencing a question within the same section:
Scheme
SCHEME@ question <questionID>
JavaScript
JS$.question.questionID
For referencing a question in a previous section of the same step:
Scheme
SCHEME@ section <sectionID questionID>
JavaScript
JS$.section.sectionID.questionID
For referencing a question on a previous step:
Scheme
SCHEME@ page <pageID sectionID questionID>
JavaScript
JS$.page.pageID.sectionID.questionID
Where:<questionID>
is the ID of the question you want to reference.<sectionID>
is the ID of the section containing the question you want to reference.<pageID>
is the ID of the step containing the question you want to reference.
Referencing other values in the underlying model
Other values, values that you might not want to display, can be accessed by a question and used in another question's condition. The basic method for accessing this information is to create a question earlier in the form to access the data you want, and then use the syntax for referencing other questions to access the resulting value.
The best practice for making the data available is:
- Create a question earlier in the model that accesses the data you want to use in a later question.
- If you do not want to show the question in that earlier section, set the visibility of either the question or the entire section to
#f
. - Access the question value through an @ question, @ section, or @ page reference as appropriate, later in the form.
Constructing conditional statements
Form definition is based on the Scheme programming language, which is a functional language related to Lisp. The basic syntax structure when creating your validation expressions, follows this general pattern:
operator first_operand second_operand
If any of the operands themselves are expressions, those expressions are embedded in parentheses that contain the same three-part structure.
For example, the following expression:
Scheme
(= "Y" (@ question hasRealEstateEquity))
JavaScript
$.question.hasRealEstateEquity === "Y"
tests whether the value of the answer to the question hasRealEstateEquity
evaluates to Y
. If it does, the overall expression gives a value of #t
(true
). Otherwise, it evaluates to #f
(false
).
Any logical, comparison, or other formula allowed by Scheme can be used in your conditions.
Details about the these and other functions, and about their syntax, can be found in the NexJ Studio help system under the NexJ Scheme Library Reference information.
Controlling whether a question is mandatory
If you want to make a question mandatory only under certain conditions, specify an expression that evaluates to a Boolean value for the required attribute on the field.
For a simple illustration, suppose you have created a form that asks the following questions:
- Does the person have equity in a house?
- If so, what is the estimated value of the house and what is the total outstanding in debts against the house (for example, mortgages)?
Assume that you want to make the answer to the second question mandatory only if the answer to the first question is Yes.
To accomplish this, code the following:
Controlling whether a question is enabled
You can conditionally enable questions in the same way that you conditionally set whether they are required. The only difference is that rather than using the following statement:
Scheme
(: required (expr))
JavaScript
required: $.expr
use
Scheme
(: enabled (expr))
JavaScript
enabled: $expr
Using the following statement:
Scheme
(: enabled #f)
JavaScript
enabled: false,
allows you to create an effectively read-only field within the form. This is useful for calculated fields, or for showing information that you want the person filling the form to see, but not change.
Controlling the visibility of questions, section, and pages
Control the visibility of a step, section, or control using the following expression:
Scheme
(: visible (expr))
JavaScript
visible ($.expr)
For example, in the following code, the answer to the first question controls the visibility of the final three. See the accompanying pictures for an illustration:
In the first instance, the Employment Status is Employed, and all three follow-on questions appear. In the second instance, the answer to the first question is Self-Employed and no information about the employer is shown. Because the field was initialized to NexJ Systems, this is the value that is stored in the form, though likely ignored by future processing down the line. In this case, the person has indicated they are retired, and no additional employment questions are asked. As with the second instance, the initialization values mean that the form, in fact, stores that the type of business is Retail, the position held was Director, Product Research, and that the employer was NexJ Systems. Again, a well-designed workflow making use of this form later ignores these values. |
Forms with dynamic approvals
As of NexJ CRM 9.5, administrators can define dynamic approvals for forms by specifying enty criteria in the Customize workspace in the classic UI (released with 8.X) for business processes. When users submit forms that have been configured for dynamic approvals, the forms are automatically directed to the required approvers.
By default, any supported element types are available for selection in the Entry Criteria superpicker for business processes in the classic UI. The following element types automatically work without any configurations:
Primitives:
bp:text
bp:textarea
bp:integer
bp:number
bp:checkbox
bp:date
bp:datetime
bp:currency
Enums:
bp:combo
bp:radio
where
clauses set on enumerations are not respected in the Entry Criteria superpicker, as where
clauses can depend on runtime values that are not available to the superpicker. Only the first 1000 entries of an enumeration are selectable in the superpicker.
In the case of bp:object
, the class specified as the "valueType" must have the "UI_COMPONENT" aspect and "UICOMPONENT_NAME" attribute set. This is required to show the correct object picker in the Entry Criteria superpicker.
bp:attachment
,
bp:image
, and
bp:label
).
National language considerations
Forms support the UTF-8 character set. When composing forms in libraries, you cannot enter non-ASCII characters directly, as they do not display properly in the form. To display unicode characters, you must use their escaped unicode values. For example, if composing a form in French and you want to include a section label that reads "connaître votre client ", enter conna\u00EEtre votre client
in your form definition.
If composing a form in a bi-directional language, enter the character symbols in a left-to-right manner. The rendering engine corrects the display to right-to-left as appropriate. For example, to write "أَلِف بَاء", (the names of the first two letters of the Arabic alphabet) enter \u0623\u064E\u0644\u0650\u0641 \u0628\u064E\u0627\u0621.
To aid in conversion of strings to escaped unicode, use an online tool such as the one available at https://r12a.github.io/app-conversion (use the JavaScript output if using this converter).
Form localization
Process Management forms let you display forms in different languages based on user preference. You can change the localization of form controls by using strings defined on the client (.js file). Form contents can be translated using strings defined on the server (.strings file) and client (.js file).
Forms with proper translations for the strings are translated based on your browser preference.
To change the localization of your forms, you must:
- Add the translation for the form controls, such as buttons and other elements of the form.
To do this, go to NexJ Studio, and:Navigate to the
client_project/mod/nexj/ui.bp/i18n
folder. You may have to create this folder if it does not already exist.- Make a copy of the
en.js
(default) file.
Theen.js
file can be found in NexJ Studio by navigating to Window > Show View > Project Explorer. - Name the <xx>.js file according to the language you want to use.
This file defines the behavior of the currency, date, and system in the language and its variant codes. You can make changes to this file through Resources > Locales.
- To add the translations for the form contents, you must first enable your project to display your preferred locale. In NexJ Studio:
- Navigate to the
client_project/mod/nexj/i18n
folder. If the folder does not already exist, add it as a new folder. - Add a locale file. Make a copy of the
en.js
file found inclient_project
/mod/nexj/i18n
/, and rename the file. - Add another translation file to
client_project/mod/nexj/ui.chart/i18n
/. You can use theen.js
file found inclient_project/mod/nexj/ui.chart/i18n
/ as a sample.
- Navigate to the
- To translate the form content, provide translations for every string used by the form in the
.strings
file located in Resources > Strings. For example, translate the strings insideen.strings
withids
(IDS_BUSINESS_PHONE
,idsc.ReportLayout.caption
, and so on), but do not end in.en
or.fr
. - Confirm that your translations are in your
client_project
.
For example, to create a form in Italian:
- Translate the form-specific buttons that show in the Process Management forms Admin Portal by:
- Going to NexJ Studio, navigate to the
client_project/mod/nexj/ui.bp/i18n
folder, and make a copy of theen.js
file which you would renameit.js
. - Translate the file contents into Italian.
- Going to NexJ Studio, navigate to the
- Add the two files containing translations for the portal application by:
- Making a copy of the
en.js
file found inclient_project/mod/nexj/i18n
/, renaming the fileit.js
, and placing it in theclient_project/mod/nexj/i18n/
folder. - Translate the file into Italian.
- Add a second translation file, this time to
client_project/mod/nexj/ui.chart/i18n
/ by making a copy of theen.js
file found in this folder, and renaming itit.js
. - Provide the Italian translation for the steps in your form.
- Making a copy of the
When creating localized text inside your forms, use a string identifier, such as idsc.Person.firstName
in the caption, captions or valueCaption field of the flow elements in the domain specific language. As an example, a first name text box with a localized caption of First Name would be:
(bp:text firstName (: caption "idsc.Person.firstName"))
To enable localization, you must modify the form template. Any change on the template will trigger a seed of the localization. All newly created templates will work with localization by default.
You cannot change language in the forms portal. This must be done by configuring client locales. See Resources.
If the user interface gets the same locale supported at a later time, the file in the Process Management project will override the user interface version. You will need to remove/modify the Process Management locale as necessary.
Registering page elements on global registry
You can reference pre-registered form libraries inside a form without having to redefine them. If you want to reference a library through bp:util:library-ref
, register elements using bp:register-flow-library.
The following example registers a new library containing a question called registered-question
, and then references it in a form called myForm
.
Using additional buttons in a page control
You can use a tool property in a page control to add a button to execute an action. The following three dynamic properties are supported:
- caption
- visible
- function
Each property can contain either a static value or multiple question references. The following examples show how to use tool property in a page or a page repeater.
Binding forms and navigating attributes
Each form is bound to a single class in the business model. By default, the attributes that questions collect data from, are the attributes of that class.
Instead of binding a Process Management form with a NexJ metaclass and associated attributes, you can bind a form with an external schema model using REST API and a JSON structure. For more information about binding a form with an external schema model, see Binding forms with an external schema model using REST APIs .
Complications can arise if the values you want to access are actually within an associated class. In this case, you must create a section specifically for accessing those attributes. Additionally, the ID of that section must match the name of the non-primary attribute whose information you want to access.
For example, consider gathering the home information address for an instance of the Person
class:
As shown in this reduced class diagram, the homeAddress
attribute is a non-primitive attribute.
Expanded, you can see more clearly that the homeAddress
attribute is an instance of the associated Address
class.
In order to access and update the homeAddres
s data for the person, create a section in your form that explicitly references the homeAddress
attribute. That section then gives you access to the attributes of the Address
class. The following code illustrates this example:
Line 27 binds the form as a whole to the Person
class. However, as illustrated in the class diagram, the homeAddress
attribute is an associated class. To access the attributes within homeAddress
, the section ID is specified to match the complex attribute.
The level to which you can traverse the class model is currently limited to one level out. Given the class that you bind your form to, you can name any of its associated classes in a section and access their primitive attributes. You cannot, however, access the associated classes of an associated class.
There is one exception to this limitation, and it is illustrated in the preceding example: the homeAddress
attribute type
is, strictly speaking, a second-level associated class to the Person
class. However, because of its use of bp:OBJECT_AS_ENUM
aspect, it behaves as if it is an enumeration, and is treated as such for the purposes of form definition.
Debugging your forms
If you want to add debugging to the server-side of your Process Management forms, add (logger’info …)
to the code, and observe the results in the Scheme console. To debug client-side code, use the Chrome debugger.
Using the progress list
When creating a multi-page form, a progress list is displayed in the form's Completion Status by default. This enables users to track their completion of required questions using a progress indicator for each page of the form, as well as a visualization of the entire form's status. This is shown in the following example:
You can disable the progress list by adding the following code, under the bp:flow
element, in your form.
Scheme
(: showProgress #f)
JavaScript
showProgress: false
The following code is for an example form that disables the progress list and contains the following sections:
- Client Details
- Addresses
- Related Parties
- Related Party - Spouse
- Related Party - Accountant
- Related Party - Referred By
- Dummy page
If you want to implement the progress list for a single page form, add the following code under the bp:flow
element in your form.
Scheme
(: showProgress #t)
JavaScript
showProgress: true
Managing your forms
Process Management forms in use, templates, template attachments, and documents are managed from the Process Management Admin Portal. You can also develop forms using the Admin Portal. Access the Admin Portal from <Application Root URL>/nexj/ui/bp-admin
. When working with a form's code on the Templates workspace in the Admin Portal, clicking the Reset button resets all values to the default values. For information about managing your forms using the Process Management Admin portal, see "Managing Process Management forms" in the Application Administration documentation.
Bypassing conflict warning messages
By default, upgrade conflicts are visible in the NexJ CRM user interface. You can configure upgrade conflicts so that users are not notified every time there are changes to a form's structure relating to pages, sections, and questions. To disable upgrade conflicts, set the cpm.showUpgradeConflicts
property to false
in the Development.properties
file.
Configuring Process Management forms using JavaScript
This section provides information specific to those configuring Process Management forms in JavaScript. JavaScript forms are configured as libraries, similar to Scheme files on the metadata level, but using a .njs file extension, enabling the NexJ Studio framework to recognize and load them while the application is running.
The following JavaScript expressions are specific to Process Management.
WHERE clauses
Because WHERE clauses in Scheme are usually in a list (a type not normally supported by JavaScript), there is a new JavaScript expression for configuring WHERE clauses:
bp.where(op, operand, value)
bp.where(=, $.value, ($.question.q0) ? "Y" : "N")
translates to: `(= (@ value) ,(if (@ question q0) "Y" "N"))
bp.where(=, $.account.primaryOwner, jsToScheme("string->oid")($.global.relatedObjectId))
translates to: `(= (@ account primaryOwner) (string->oid ,(@ global relatedObjectId)))
For complex WHERE clauses that are not supported by bp:where
, use the supported Scheme literal expressions in order to use Scheme WHERE clauses as JavaScript.
bp.objects(holdings,
{
caption: "Available Holdings",
valueType: "Holding",
//Use a Scheme where clause in the JavaScript configuration
where: jsToScheme("`(any (= (@ account primaryOwner) (string->oid ,(@ global relatedObjectId))))")
}
)
Scheme literals
To implement any logic that cannot be supported with JavaScript, you can embed Scheme expressions into the JavaScript. Use the jsToScheme()
function to wrap stringified Scheme expressions so they can be called inline within existing JavaScript.
bp.flow(
{
title: "Simple CPM demo",
$onSubmit: jsToScheme("(logger 'info \"[OnSubmit] flow has been submitted\")")
},
bp.page(p1, ...)
)
In order to access any external identifiers or identifiers written in Scheme, use the sequence #("<identifier name>")
as shown below.
bp.flow(
{
title: "Sample Form with NexJ Model Bind",
bind: #("Person")
},
bp.page(p1,
bp.section(s1,
bp.text(firstName),
bp.text(lastName)
)
)
)
Null is true
Because of how JavaScript is compiled to Scheme expressions and evaluated in a Scheme interpretor, null values will return as true
. To avoid any undesired behavior, it is recommended that you explicitly check for null values in the configuration.
bp.text(q1, {
//this will be true, even when q0 is empty
visible: this.question.q0,
//do this instead:
enabled: this.question.q0 != null
}
)
NexJ JavaScript vs Scheme resources
Both .njs and .scm forms can coexist under the libraries folder in the project metadata. However, the parser cannot currently differentiate a Scheme vs JavaScript resource when both file names and paths are the same.
Avoid sharing the same name between Scheme and JavaScript resources.
JavaScript limitations
The following statements are not supported by JavaScript and will throw errors:
- Import statement
- For ... in statement
- For ... var... in statement
- With statement
- Debugger statement
- Property getter and Property setter
- Postfix decrement operation (–)
- Postfix increment operation (++)
- Typeof operation
- Delete operation
- The >>> and >>>= operator
- Void
- Statements using any future reserved words
- Import of JavaScript modules
For more information, see NexJ JavaScript differences .
Working in a sandbox
When you open the Process Management Admin portal, the Templates tab is in the run-time configurability sandbox. Working in this space, you can execute forms for independent evaluation, monitoring or testing. You do not need to stop and start servers to view any changes you make. No redeployment is needed. Build your forms using Scheme or JavaScript. When you are ready to implement the form, copy the code, paste and check it into an SCM or NJS library file in NexJ Studio.
Forms created/modified in the sandbox space run in the same manner as forms deployed from source control. As such, forms can contain actions that modify data in the system. This feature should be limited to administrator-type users who understand the consequences of any actions added/modified on a form and should only be used in a non-production environment as a tool to test form changes at runtime.
Creating forms in your sandbox
To create a new form:
- Click the Add icon .
- Click the Edit icon to change the name of the form.
- Choose either run-time JavaScript (NJS) or Scheme (SCM) code.
To edit an existing form, click on the code.
When you create a form, you are presented with a split workspace; one half is code (either JavaScript or Scheme, depending on which you have selected), the other half is a preview of what the form will look like. The code view includes code smart formatting such as auto-completion of Process Management functions, auto indentation and alignments, and keywords formatting.
Changing font size is not recommended as the controls are built to align with Material Design guidelines. Color changes can be achieved by changing the theme at the project level.
After you have made changes to the code, click the Preview button to refresh the form. When you are satisfied with the form's functions and appearance, click Save to publish the form.
In the code for forms using references, helper functions must be situated above the flow, and the entire code must be wrapped in another function following JavaScript and Scheme conventions.
Additional examples
The following examples demonstrate some possible configurations.
Forms using references
JavaScript configuration using helper functions
; Helper function for returning
; the address section
(define (testAddressSection sectionNumber)
(bp:section addrs
(: caption "Addresses")
(: number sectionNumber)
(: captions
(define a ())
(if (not (null? (@ question name)))
(set! a (string-append "(" (@ question name) ") " (@ question address1)))
;else
(set! a (@ question address1))
)
;return
a
)
(: collection #t)
(bp:text name (: caption "Address Name"))
(bp:combo type
(: caption "Address Type")
(: options (collection "Home" "Business"))
)
(bp:text address1 (: caption "Address Line 1"))
(bp:text address2 (: caption "Address Line 2"))
(bp:combo country
(: caption "Country")
(: options (collection "Canada" "USA" "UK" "China"))
)
(bp:text zip (: caption "Zip Code"))
)
)
; Helper function for the address page
(define (testAddressPage)
(bp:page Addresses
(: caption "Addresses")
(: number "2")
(testAddressSection "S1")
)
)
(bp:register-flow "testFormDSL" #t
(bp:flow
(bp:page Client_Details
(: caption "Client Details")
(: number "1")
(bp:section profile
(: caption "Profile")
(bp:text firstName (: caption "First Name"))
(bp:text lastName (: caption "Last Name"))
(bp:text fullName
(: caption "Full Name")
(: value (string-append (@ question firstName) " " (@ question lastName)))
)
(bp:date birthTime (: caption "Date of Birth"))
(bp:object user
(: caption "User 1")
(: where `(= (@ loginName) "nexjsa"))
(: valueType "User")
(: valueCaption "fullName")
(: order "loginName")
(: filterPath "loginName")
)
(bp:objects users
(: caption "User 2")
(: where '(= (@ loginName) "nexjsa"))
(: valueType "User")
(: valueCaption "fullName")
(: order "loginName")
(: filterPath "loginName")
)
)
)
(testAddressPage)
(bp:page misc
(: caption "Misc. Questions")
(: number "3")
(bp:section s1
(: caption "Calculated fields")
(bp:text addr0Line1
(: caption "Address 0, Line 1")
(: value (@ section '(addrs . 0) address1))
)
(bp:radio selectOption
(: caption "Select an option")
(: options (collection "Yes" "No"))
(: required #t)
)
(bp:text selectedOption
(: caption "The selected option is:")
(: enabled #t)
(: value (if (= (@ question selectOption) "Yes") "Y" "N"))
)
)
)
)
)
/* Helper function for returning
the address section */
function testAddressSection(sectionNumber) {
return bp.section(addrs,
{
caption: "Addresses",
number: sectionNumber,
captions: function() {
var a;
if (this.question.name != null)
a = "(" + this.question.name + ") " + this.question.address1;
else a = this.question.address1;
return a;
},
collection: true
},
bp.text(name, {caption: "Address Name"}),
bp.combo(type,
{
caption: "Address Type",
options: ["Home", "Business"]
}
),
bp.text(address1, {caption: "Address Line 1"}),
bp.text(address2, {caption: "Address Line 2"}),
bp.combo(country,
{
caption: "Country",
options: jsToScheme("(collection \"Canada\"" +
"\"USA\"" +
"\"UK\"" +
"\"China\")"
)
}
),
bp.text(zip, {caption: "Zip Code"})
);
};
//Helper function for the address page
function testAddressPage() {
return bp.page(Addresses,
{
caption: "Addresses",
number: "2"
},
testAddressSection("S1"));
};
bp.registerFlow("testFormJDSL", true,
(function() {
return bp.flow(
bp.page(Client_Details,
bp.property(caption, "Client Details"),
bp.property("number", "1"),
bp.section(profile,
{caption: "Profile"},
bp.text(firstName, {caption: "First Name"}),
bp.text(lastName, {caption: "Last Name"}),
bp.text(fullName,
{
caption: "Full Name",
value: function() {
return $.question.firstName + " " + $.question.lastName;
}
}
),
bp.date(birthTime, {caption: "Date of Birth"}),
bp.object(user,
{
caption: "User 1",
where: bp.where("=", $.loginName, "nexjsa"),
valueType: "User",
valueCaption: "fullName",
order: "loginName",
filterPath: "loginName"
}
),
bp.objects(users,
{
caption: "User 2",
where: jsToScheme("'(= (@ loginName) \"nexjsa\")"),
valueType: "User",
valueCaption: "fullName",
order: "loginName",
filterPath: "loginName"
}
)
)
),
testAddressPage(),
bp.page(misc,
{
caption: "Misc. Questions",
number: "3"
},
bp.section(s1,
{caption: "Calculated fields"},
//try out both Scheme and javascript for the same formula
bp.text(addr0Line1js,
{
caption: "Address 0, Line 1 (js)",
value: function () {
return this.section.addrs[0].address1;
}
}
),
bp.text(addr0Line1scm,
{
caption: "Address 0, Line 1 (scm)",
value: jsToScheme("(@ section '(addrs . 0) address1)")
}
),
bp.radio(selectOption,
{
caption: "Select an option",
options: ["Yes", "No"],
required: true
}
),
bp.text(selectedOption,
{
caption: "The selected option is:",
enabled: false,
value: this.question.selectOption === "Yes" ? "Y" : "N"
}
)
)
)
);
})()
);