This block provides an editable form. Alternatively, it can also show a form in a view-only mode.
It's also possible to have an editable form with certain fields in read-only and vice versa.
For more information about form design, please see Forms.
The form block has the following parameters:
Property | Example Usage | Description |
---|---|---|
Form | Change password (/public/xxas/emp/view/changepassword.xml) | Reference to the form definition. For more details about form design please see Forms. |
Read-only | Checked Unchecked | When ticked, the forms run in read-only mode (although individual fields can still be made editable) |
Form handler | xxas_login_pkg.change_password | The PL/SQL procedure to call when a form is submitted. This procedure should validate the form entry and save the form information. See Form handler. |
Personalization formula | XXAX_SOME_FAST_FORMULA | Optional Fast Formula to set form properties using advanced conditions. Only applies when in read-only. See Form personalization formulas. |
Edit personalization formula | XXX_SOME_FAST_FORMULA | Optional Fast Formula to set form properties using advanced conditions. Does not apply when read-only. See Form personalization formulas. |
Redirect to | login | Set this to optionally redirect to this page after the form has been successfully saved. The value is the Page Id of the page you wish to redirect to. |
Pre-populating a form
There are a number of reasons why you want to pre-populate a form:
- To set a hidden form field, such as a PERSON_ID.
- To populate a partially-completed form or a previously submitted form that is being edited
- To populate a form in view-only mode
- To provide defaults into some fields
In practice, you will want to pre-populate most forms. Forms that are not pre-populated tend to be anonymous forms such as a login form or a forgotten password form.
The object on the block continues to be used for Block access control but it also doubles up as the data provider to pre-populate the form. It is then assumed that the object returns a single database row when the block applies the object Id restrictions that join on its primary key(s).
The data object will obviously return the primary key columns defined (otherwise you wouldn't be able to define them as primary keys); the object should also return the values for the form fields that should be pre-populated. Let's look at an example:
Here the SQL (which would be a database view linked to the Object) contains PERSON_ID, which is the object's primary key. It also returns FIRST_NAME and LAST_NAME. If the form had fields with the Ids PERSON_ID, FIRST_NAME, and LAST_NAME then all 3 values would automatically default into the form. If the form had a fourth field called EMAIL_ADDRESS this would not be pre-populated because it is not returned by the object. Similarly, if the form defined a field with the Id SURNAME this would also not be pre-populated because the Id of the field does not match with the column names of the object.
Auto-discovery of Formula Contexts
Auto-discovery of Formula Contexts occurs when a personalization formula is used. A Fast Formula is linked to a Formula Type and the Formula Type determines which contexts are required. The Formula Types available are:
- Applaud Person. This uses the PERSON_ID, DATE_EARNED, and BUSINESS_GROUP_ID context.
- Applaud Assignment. This uses the ASSIGNMENT_ID, DATE_EARNED, and BUSINESS_GROUP_ID context.
- Applaud Organization. This uses the ORGANIZATION_ID and DATE_EARNED context.
- Applaud Business Group. This uses the BUSINESS_GROUP_ID and DATE_EARNED context.
To execute the Fast Formula, the app needs all of the contexts required by that Formula Type. The app will attempt to auto-discover these contexts from the block's data object. It uses these rules to achieve this:
- If the formula type is Applaud Person it looks for a PERSON_ID value in the object. If it finds one it sets this as the PERSON_ID context. If one doesn't exist but an ASSIGNMENT_ID value exists in the object, it derives the PERSON_ID context from this ASSIGNMENT_ID. If no ASSIGNMENT_ID context exists, it will use the PERSON_ID of the logged-on person.
- If the formula type is Applaud Assignment it expects an ASSIGNMENT_ID value in the object and sets this as the ASSIGNMENT_ID context.
- If the formula type is Applaud Assignment and no ASSIGNMENT_ID value is in the object it will error.
- It looks for a BUSINESS_GROUP_ID value in the object. If one is not found it derives the BUSINESS_GROUP_ID from the person or assignment.
- If the Formula Type is Applaud Organization it expects an ORGANIZATION_ID value in the object.
- For DATE_EARNED it first looks for a DATE_EARNED date value in the object. If that's not there it looks for an EFFECTIVE_DATE date value in the object. Finally, it defaults DATE_EARNED to sysdate if neither of the previous values is in the object.
Auto-save
Forms have auto-save automatically built into them. That means the form state is temporarily saved into the user's browser or on the user's device when they navigate away from a page with a form on it. When a user returns to a form that has been auto-saved they will see a bar.
The user can choose to reset the form and clear the data or use the arrow to dismiss the bar.
Some notes on auto-save:
- It is disabled for forms that run in view-only mode
- It is disabled for other seeded forms, such as login, change password and reset password
- The time-to-live of the auto-saved data is typically very short. The moment a user performs any update interaction inside the app (technically an HTTP PUT, POST, or DELETE) all auto-saved form data is removed
- Auto-save does not save password fields under any circumstances
- Some forms use a server-side auto-save. Unlike the above auto-save where data is saved on the user's browser or device, server-side auto-save persists data on the server so that it is preserved regardless of device or browser and likely has a longer time-to-live. In these cases, the client-side auto-save is disabled and the user will not see the above bar.
Form submission security
When a form is submitted, the app will confirm access to submit the form. It does this as follows:
- It looks at the primary key columns defined on the block's objects
- It retrieves the values of these primary key columns from the form's hidden fields
- It tests access to the block using the retrieved values
For example, if primary key column 1 is PERSON_ID, the app will look for a field with the id PERSON_ID and test access to the block using that value.
The form definition must include Hidden fields for each primary key defined in the Object. For example, if the Object defines primary key columns 1 and 2 as PERSON_ID and ASSIGNMENT_ID respectively, the form definition must include hidden fields with the Ids PERSON_ID and ASSIGNMENT_ID. When the form is submitted, the app will use these hidden form values to again validate access to the block; this prevents malicious users from calling the REST APIs directly with unauthorized values.
Form handler
The form handler is specified in the blocks settings. The form handler essentially takes the raw input from the form, validates it, and saves it.
The form handler must conform to this specification:
p_function_id is the Id of the block's function. p_form_data_xml is the submitted form data converted from JSON to XML. p_user_submitted is a Y or N flags indicating whether the user pressed submit or whether the form was auto-saved. p_response_xml is a response document that may contain informational messages, error messages, or redirection and reload properties.
Here's an example of a form handler:
(p_function_id IN NUMBER
,p_form_data_xml IN XMLType
,p_user_submitted IN VARCHAR2 DEFAULT 'N'
,p_response_xml OUT XMLType);
p_function_id is the Id of the block's function. p_form_data_xml is the submitted form data converted from JSON to XML. p_user_submitted is a Y or N flag indicating whether the user pressed submit or whether the form was auto-saved. p_response_xml is a response document that may contains informational messages, error messages, or redirection and reload properties.
Here's an example of a form handler:
PROCEDURE form_handler
(p_function_id IN NUMBER
,p_form_data_xml IN XMLType
,p_user_submitted IN VARCHAR2 DEFAULT 'N'
,p_response_xml OUT XMLType)
IS
l_data xxas_emp_view_ui_pkg.xxas_data_table;
l_person_id number;
l_some_value varchar2(400);
BEGIN
--
-- A handy utility function to convert the XML document into a linear set of
-- name/value pairs. This utility function does not convert select-multiple values, or
-- child values from a multi-row.
-- You'll need to extract these children yourself using XMLTable in a SELECT statement.
--
l_data := xxas_emp_view_ui_pkg.transform_jsonxml_form_data
(p_form_data_xml);
IF l_data.exists('PERSON_ID') THEN
l_person_id := to_number(l_data('PERSON_ID'));
END IF;
IF l_data.exists('SOME_VALUE') THEN
l_some_value := substrb(l_data('SOME_VALUE'), 1, 400);
END IF;
--
-- Raise an error message based on various conditions.
-- The last parameter to xxas_com_msg_type is an array of name-value
-- message tokens and can be used like this:
-- ,p_message => xxas_com_msg_type
-- (xxas_util_pkg.app, 'XXX_SOME_FND_MSG'
-- ,xxas_com_msg_token_varray
-- (xxas_com_msg_token_type('TOKEN1', 'Token value one')
-- ,xxas_com_msg_token_type('TOKEN2', 'Token value two'))));
--
IF l_some_value = 'SOME_INVALID_VALUE' THEN
xxas_com_api_result_pkg.raise_user_error
(p_package_name => 'MY_PACKAGE'
,p_proc_name => 'FORM_HANDLER'
,p_message => (xxas_com_msg_type
(xxas_util_pkg.app, 'XXX_SOME_FND_MSG', null)));
END IF;
some_procedure
(p_person_id => l_person_id
,p_some_value => l_some_value);
--
-- The 'self' argument here tells the UI to reload the current page.
-- If the current page has an open modal window it will close that
-- window.
--
p_response_xml := xxas_com_msg_pkg.get_success_message_xml
(xxas_com_msg_type
(xxas_util_pkg.app, 'XXAS_COM_CHNG_SAVE', null)
,'self');
END form_handler;