Validating and submitting data to CRM

The CRM Helper Framework is a JavaScript library designed to simplify validation and data submission tasks in Power Pages, particularly for interacting with Dynamics 365. It provides a set of reusable functions to handle form validation, data submission to CRM entities, and user experience enhancements like error handling, button state management, and loading spinners. This framework is pre-installed with the base portal, making it readily available for you to use in your front-end development.

This page covers how to set up validation, use the framework's functions, and provides practical examples for common scenarios.

Quick reference

Quick reference of available functions
Function Description
ValidateData(inputObjArr) Validates user input and displays error messages
WebApi.ConstructData(inputsObj) Constructs a data object for CRM submission
WebApi.Update(recordId, entityName, dataObj) Updates an existing CRM record
WebApi.Create(entityName, dataObj) Creates a new CRM record
WebApi.Delete(recordId, entityName) Deletes a CRM record
WebApi.Retrieve(entityName, searchQuery) Retrieves records from a CRM table
WebApi.CallCloudFlow(flowApiId, data) Calls a cloud flow and returns the response
WebApi.UploadFile(inputName, entityName, entityId) Uploads a file to a file column on a record
WebApi.ProcessFileAndUploadToNotes(inputName, entityName, entityId) Uploads a file as a note attachment
DisableButton(buttonID) / EnableButton(buttonID) Disables or enables a button
ShowLoadingSpinner(spinnerID) / HideLoadingSpinner(spinnerID) Shows or hides a loading spinner

Creating your validation data object

Before using the framework's validation functions, you must create a validation data object. This object is an array of input objects, where each object describes a form field to validate. You can define a single input as [{...}] or multiple inputs as [{...},{...},{...}].

Base properties for all input types
Key Type Description
identifier String The schema name of the target CRM field (e.g., dfe_firstname).
type String The type of input displayed on the page. Supported types: text, text-area, email, tel, number, whole-number, decimal-number, money, date, radio, checkbox, select, file
friendlyName String A user-friendly name for the input, used in error messages.
e.g. "your first name" will display as "Enter your first name".
required Boolean true if the field is required, false otherwise.

InputObj examples by input type

Expand the sections below to see complete inputObj examples, when to use each configuration, and what the ConstructData function outputs.

Text input

Standard text input

Use for single-line text fields like names, references, or short answers.

{
  identifier: "dfe_firstname",
  type: "text",
  friendlyName: "your first name",
  required: true
}

ConstructData output:

{ "dfe_firstname": "John" }

Text input for record search (lookup)

Use when you need to search for a record by a text value (e.g. finding an application by reference number). The framework validates that the record exists and stores the record ID for submission.

{
  identifier: "dfe_applicationref",
  type: "text",
  friendlyName: "application reference",
  required: true,
  targetType: "lookup",
  targetEntity: "dfe_applications",
  targetEntityPrimaryKey: "dfe_applicationid",
  targetEntitySearchField: "dfe_referencenumber"
}

ConstructData output (creates an OData bind to the found record):

{ "dfe_applicationref@odata.bind": "/dfe_applications(abc123-def456-...)" }

Textarea

Use for multi-line text fields. Supports character count and word count validation when used with the GOV.UK character count component.

{
  identifier: "dfe_description",
  type: "text-area",
  friendlyName: "a description",
  required: true
}

ConstructData output:

{ "dfe_description": "This is the user's multi-line input..." }

If using with character count, add data-maxlength or data-maxwords to the .govuk-character-count container element.

Email input

Use for email address fields. Validates email format automatically.

{
  identifier: "emailaddress1",
  type: "email",
  friendlyName: "your email address",
  required: true
}

ConstructData output:

{ "emailaddress1": "user@example.com" }

Telephone input

Use for UK telephone numbers. Validates UK phone number formats including mobile, landline, and international (+44) formats.

{
  identifier: "telephone1",
  type: "tel",
  friendlyName: "your telephone number",
  required: true
}

ConstructData output:

{ "telephone1": "07700 900 982" }

Number inputs

Whole number

Use for integer values. Validates that the input is a whole number without decimals.

{
  identifier: "dfe_quantity",
  type: "whole-number",
  friendlyName: "quantity",
  required: true
}

ConstructData output:

{ "dfe_quantity": 5 }

Decimal number

Use for values that require decimal places.

{
  identifier: "dfe_percentage",
  type: "decimal-number",
  friendlyName: "percentage",
  required: true
}

ConstructData output:

{ "dfe_percentage": 75.5 }

Money

Use for currency values. Similar to decimal-number but formats error messages with currency symbols.

{
  identifier: "dfe_amount",
  type: "money",
  friendlyName: "amount",
  required: true
}

ConstructData output:

{ "dfe_amount": 1500.00 }

Number (general)

Use when you need basic number validation without whole/decimal restrictions.

{
  identifier: "dfe_value",
  type: "number",
  friendlyName: "value",
  required: true
}

You can add minvalue, maxvalue, minchar, and maxchar attributes to the HTML input element for additional validation.

Date input

Date to date/time field (default)

Use when submitting to a CRM date/time field. Outputs ISO format.

{
  identifier: "dfe_dateofbirth",
  type: "date",
  friendlyName: "your date of birth",
  required: true
}

ConstructData output:

{ "dfe_dateofbirth": "1990-05-15" }

Date to text field

Use when submitting to a CRM text field instead of a date/time field. Outputs DD/MM/YYYY format.

{
  identifier: "dfe_datetext",
  type: "date",
  friendlyName: "the date",
  required: true,
  targetType: "text"
}

ConstructData output:

{ "dfe_datetext": "15/05/1990" }

Radio inputs

Radio to boolean field

Use for Yes/No questions that map to a CRM Yes/No (boolean) field. Radio values should be 1 for Yes and 0 for No.

{
  identifier: "dfe_agreeterms",
  type: "radio",
  friendlyName: "if you agree to the terms",
  required: true,
  targetType: "bool"
}

HTML:

<input type="radio" name="dfe_agreeterms" value="1"> Yes
<input type="radio" name="dfe_agreeterms" value="0"> No

ConstructData output:

{ "dfe_agreeterms": true }

Radio to choice column (option set)

Use when mapping to a CRM choice column. Radio values should be the option set integer values.

{
  identifier: "dfe_contactpreference",
  type: "radio",
  friendlyName: "your contact preference",
  required: true,
  targetType: "select"
}

HTML:

<input type="radio" name="dfe_contactpreference" value="100000000"> Email
<input type="radio" name="dfe_contactpreference" value="100000001"> Phone
<input type="radio" name="dfe_contactpreference" value="100000002"> Post

ConstructData output:

{ "dfe_contactpreference": 100000001 }

Radio to lookup field

Use when each radio option represents a different record in a related table. Radio values should be the GUIDs of the related records.

{
  identifier: "dfe_region",
  type: "radio",
  friendlyName: "your region",
  required: true,
  targetType: "lookup",
  targetEntity: "dfe_regions"
}

HTML:

<input type="radio" name="dfe_region" value="abc123-..."> North
<input type="radio" name="dfe_region" value="def456-..."> South

ConstructData output:

{ "dfe_region@odata.bind": "/dfe_regions(abc123-...)" }

Radio to text field

Use when you want to store the label text of the selected option rather than a value.

{
  identifier: "dfe_selectedoption",
  type: "radio",
  friendlyName: "an option",
  required: true,
  targetType: "text"
}

ConstructData output (stores the label text):

{ "dfe_selectedoption": "Option A" }

Radio to split fields

Use when different radio selections should write to different CRM fields. The id attribute of each radio becomes the field name.

{
  identifier: "contact-method",
  type: "radio",
  friendlyName: "how to contact you",
  required: true,
  targetType: "split-field",
  targetFieldType: "text"
}

HTML:

<input type="radio" name="contact-method" id="dfe_emailaddress" value="user@example.com"> Email
<input type="radio" name="contact-method" id="dfe_telephone" value="07700900123"> Phone

ConstructData output (if Email selected):

{ "dfe_emailaddress": "user@example.com" }

ConstructData output (if Phone selected):

{ "dfe_telephone": "07700900123" }

Checkbox inputs

Checkbox to boolean field

Use for a single checkbox that maps to a Yes/No field. Checkbox value should be 1.

{
  identifier: "dfe_marketingconsent",
  type: "checkbox",
  friendlyName: "if you consent to marketing",
  required: true,
  targetType: "bool"
}

ConstructData output:

{ "dfe_marketingconsent": true }

Checkbox to choice column (single selection)

Use for a single checkbox that sets a choice column value.

{
  identifier: "dfe_status",
  type: "checkbox",
  friendlyName: "the status",
  required: true,
  targetType: "select"
}

ConstructData output:

{ "dfe_status": 100000001 }

Checkbox to multi-select choice column

Use for multiple checkboxes that map to a multi-select choice column (choices). Values are submitted as a comma-separated string.

{
  identifier: "dfe_interests",
  type: "checkbox",
  friendlyName: "your interests",
  required: true,
  targetType: "multi-select"
}

HTML:

<input type="checkbox" name="dfe_interests" value="100000000"> Sports
<input type="checkbox" name="dfe_interests" value="100000001"> Music
<input type="checkbox" name="dfe_interests" value="100000002"> Travel

ConstructData output (multiple selected):

{ "dfe_interests": "100000000,100000001" }

Checkbox to text field

Use when you want to store the label text of selected options.

{
  identifier: "dfe_selecteditems",
  type: "checkbox",
  friendlyName: "items",
  required: true,
  targetType: "text"
}

ConstructData output (single selection):

{ "dfe_selecteditems": "Sports" }

ConstructData output (multiple selections - returns array):

{ "dfe_selecteditems": ["Sports", "Music"] }

Select (dropdown) inputs

Select to choice column (option set)

Use when mapping to a CRM choice column. Option values should be the option set integer values.

{
  identifier: "dfe_title",
  type: "select",
  friendlyName: "your title",
  required: true,
  targetType: "select"
}

HTML:

<select id="dfe_title">
  <option value="">Select a title</option>
  <option value="100000000">Mr</option>
  <option value="100000001">Mrs</option>
  <option value="100000002">Ms</option>
</select>

ConstructData output:

{ "dfe_title": 100000000 }

Select to lookup field

Use when each option represents a record in a related table. Option values should be the GUIDs.

{
  identifier: "dfe_country",
  type: "select",
  friendlyName: "your country",
  required: true,
  targetType: "lookup",
  targetEntity: "dfe_countries"
}

ConstructData output:

{ "dfe_country@odata.bind": "/dfe_countries(abc123-...)" }

Select to text field

Use when you want to store the display text of the selected option.

{
  identifier: "dfe_selectedcountry",
  type: "select",
  friendlyName: "your country",
  required: true,
  targetType: "text"
}

ConstructData output:

{ "dfe_selectedcountry": "United Kingdom" }

File input

Use for file upload fields. Validates file extension, size, count, and performs virus scanning.

{
  identifier: "dfe_document",
  type: "file",
  friendlyName: "a document",
  required: true,
  allowedFileTypes: ["pdf", "doc", "docx"],
  maxFileSize: 10,  // MB
  fileLimit: 3      // maximum number of files
}

File inputs are validated but not included in ConstructData output. After record creation, use one of the following functions to upload files:

  • DfEPortal.WebApi.UploadFile - Use when uploading to a file column or image column directly on the entity record.
  • DfEPortal.WebApi.ProcessFileAndUploadToNotes - Use when uploading files as note attachments (annotations) linked to the record.

Available functions

The CRM Helper Framework provides core functions for validation and data submission, as well as helper functions for enhancing the user experience.

DfEPortal.ValidateData(inputObjArr, errorContainer)

Error messages on the page are handled within this function.
Purpose
Validates user input data on the webpage
Parameters
  • inputObjArr: Array - the validation data object array (see above).
  • errorContainer: String (optional) - CSS selector for the error summary container. Defaults to #error-summary-container.
Returns
A boolean value (true or false) indicating whether data has passed validation.
Example scenarios
  • Validating a contact form to ensure all required fields (e.g., first name, email) are filled correctly.
  • Checking a date input to confirm it's a valid date before submission.
  • Ensuring a search field matches a record in the CRM before proceeding (e.g., finding an application by reference number)

DfEPortal.WebApi.ConstructData(inputsObj)

Purpose
Constructs a data object for submission to the CRM via the Power Pages WebApi.
Parameters
Array of input objects - the validated input object array.
Returns
The constructed data object.
Example scenarios
  • Preparing form data (e.g. first name, last name) for submission to a contact entity.
  • Formatting a date input into an ISO string for a CRM date/time field.
  • Mapping a radio input to a boolean value for a CRM field.

DfEPortal.WebApi.Update(recordId, entityName, dataObj)

Purpose
Updates an existing record in the CRM.
Parameters
  • recordId: String - The ID of the record to update.
  • entityName: String - Schema name of the table to update (e.g. contact).
  • dataObj: Object - Data object constructed by DfEPortal.WebApi.ConstructData.
Returns
A promise that resolves on success or rejects with an error.
Example scenarios
  • Updating a contact's email address after the user edits their profile.
  • Modifying an application record with new details from a form submission.

DfEPortal.WebApi.Create(entityName, dataObj)

Purpose
Creates a new record in the CRM.
Parameters
  • entityName: String - Schema name of the table (e.g. lead).
  • dataObj: Object - Data object constructed by DfEPortal.WebApi.ConstructData.
Returns
A promise that returns the ID of the created record on success or rejects with an error.
Example scenarios
  • Creating a new lead record when a user signs up.
  • Adding a new contact record from a "Contact Us" form submission.

DfEPortal.WebApi.Delete(recordId, entityName)

Purpose
Deletes a record from the CRM.
Parameters
  • recordId: String - ID of the record to delete.
  • entityName: String - Schema name of the table.
Returns
A promise that resolves on success or rejects with an error.
Example scenarios
  • Deleting a draft application record when a user cancels their submission.
  • Removing a contact record when a user requests account deletion.

DfEPortal.WebApi.Retrieve(entityName, searchQuery)

Purpose
Retrieves records from a CRM table using an OData query.
Parameters
  • entityName: String - Schema name of the table (e.g. contacts).
  • searchQuery: String - OData query string (e.g. ?$select=firstname,lastname&$filter=emailaddress1 eq 'test@example.com').
Returns
A promise that returns the query results on success or rejects with an error.
Example scenarios
  • Fetching a list of applications for the current user.
  • Checking if a record exists before creating a duplicate.
  • Populating a dropdown with data from a CRM table.

DfEPortal.WebApi.CallCloudFlow(flowApiId, data)

Purpose
Calls a Power Automate cloud flow and returns the response. Useful for complex business logic that cannot be performed client-side.
Parameters
  • flowApiId: String - The API ID of the cloud flow to call.
  • data: Object - Data to pass to the cloud flow as input parameters.
Returns
A promise that returns the cloud flow's response object on success or rejects with an error.
Example scenarios
  • Checking if an application already exists before allowing the user to proceed.
  • Performing complex validation that requires server-side data.
  • Triggering a workflow that sends notifications or creates related records.

DfEPortal.WebApi.UploadFile(inputName, entityName, entityId)

Purpose
Uploads a file to a file-type or image-type column directly on the entity record.
Parameters
  • inputName: String - The ID of the file input element (also used as the file column schema name).
  • entityName: String - Schema name of the table (e.g. dfe_applications).
  • entityId: String - The ID of the record to attach the file to.
Returns
A promise that resolves on success or rejects with an error.
When to use
Use this function when your entity has a file column or image column and you want to store the file directly on the record. This is the modern approach using Dataverse file columns.
Example scenarios
  • Uploading a profile photo to an image column on a contact record.
  • Attaching a PDF document to a file column on an application record.

DfEPortal.WebApi.ProcessFileAndUploadToNotes(inputName, entityName, entityId)

Purpose
Uploads a file as a note (annotation) attachment linked to the entity record.
Parameters
  • inputName: String - The ID of the file input element.
  • entityName: String - Schema name of the table (e.g. dfe_applications).
  • entityId: String - The ID of the record to attach the note to.
Returns
A promise that resolves with the file object on success or rejects with an error.
When to use
Use this function when you want to store files as note attachments in the annotations table, linked to the parent record. This is the traditional approach and is useful when you need to attach multiple files to a single record or when file columns are not available.
Example scenarios
  • Uploading multiple supporting documents to an application.
  • Attaching evidence files that need to be viewable in the CRM timeline.

DfEPortal.DisableButton / EnableButton

Purpose
Disables or enables a button on the page to prevent multiple submissions.
Parameters
buttonID: String - The ID of the button (e.g. submit-btn).
Returns
None.
Example scenarios
  • Disabling a submit button during form processing to prevent double submissions.
  • Re-enabling the button if an error occurs, allowing the user to retry.

DfEPortal.ShowLoadingSpinner / HideLoadingSpinner

Purpose
Shows or hides a loading spinner to indicate processing.
Parameters
spinnerID: String - The ID of the spinner element (e.g. loading-spinner).
Returns
None.
Example scenarios
  • Showing a spinner while submitting data to the CRM.
  • Hiding the spinner when submission completes or fails.

Code examples

Below are practical examples demonstrating how to use the CRM Helper Framework in Power Pages. These examples assume the framework is included in your page (pre-installed with the base portal).

Example 1: Updating a Contact record

This example validates a form with first name and last name fields, then updates an existing contact record in the CRM.

{% extends 'DfE/Layouts/2ColumnWideLeft' %}

{% block title %}
  {% assign title = page.title | split: ":" %}
  <h1 class="govuk-heading-l">
    <span class="govuk-caption-l">{{ title[0] }}</span>
    {{ title[1] }}
  </h1>
{% endblock %}

{% block main %}
  {% unless user %}
    <script>
      window.location.href="{{ sitemarkers['DfE/Error/AccessDenied'].url }}"
    </script>
  {% endunless %}

  {% unless params.contactid %}
    <script>
      window.location.href="{{ sitemarkers['DfE/Error/RecordNotFound'].url }}"
    </script>
  {% endunless %}

  <div class="govuk-form-group">
    <label class="govuk-label" for="firstname">
      What is your first name?
    </label>
    <input class="govuk-input" id="firstname" name="firstname" type="text">
  </div>

  <div class="govuk-form-group">
    <label class="govuk-label" for="lastname">
      What is your last name?
    </label>
    <input class="govuk-input" id="lastname" name="lastname" type="text">
  </div>

  <button type="submit" id="submit-btn" class="govuk-button" data-module="govuk-button">Continue</button>

  <div id="loading-spinner" class="loader govuk-visually-hidden"></div>

  <script type="text/javascript">
    // Define the validation data object
    const inputObj = [
      {
        identifier: "firstname",
        type: "text",
        friendlyName: "your first name",
        required: true,
        resolve: false
      },
      {
        identifier: "lastname",
        type: "text",
        friendlyName: "your last name",
        required: true,
        resolve: false
      }
    ];

    // Attach event handler to the submit button
    $("#submit-btn").on("click", async function(event) {
      event.preventDefault();

      // Disable the button and show the spinner
      DfEPortal.DisableButton("submit-btn");
      DfEPortal.ShowLoadingSpinner("loading-spinner");

      try {
        // Validate the input data
        await DfEPortal.ValidateData(inputObj);

        // Construct the data object for submission
        const dataObj = await DfEPortal.WebApi.ConstructData(inputObj);

        // Retrieve the contact ID (e.g. from a URL parameter using Liquid)
        const contactId = "{{ params.contactid }}";
        const entityName = "contact";

        // Update the contact record
        await DfEPortal.WebApi.Update(contactId, entityName, dataObj);

        // Redirect or notify the user of success
        alert("Contact updated successfully!");
        // Optionally redirect: window.location.href = "{{ sitemarkers['NEXT_PAGE_SITEMARKER'].url | add_query: 'contactid', params.contactid }}";

      } catch (error) {
        console.error("Error:", error);
        // The framework automatically displays user-friendly error messages
        // Re-enable the button and hide the spinner
        DfEPortal.EnableButton("submit-btn");
        DfEPortal.HideLoadingSpinner("loading-spinner");
      }
    });
  </script>
{% endblock %}

Explanation:

  • The inputObj array defines validation rules for the first name and last name fields.
  • The button is disabled, and a spinner is shown during processing.
  • ValidateData checks that both fields are filled (since required: true).
  • ConstructData prepares the data for submission to the CRM.
  • Update submits the data to the contact entity. If successful, a success message is shown; otherwise, the framework displays an error, and the button/spinner are reset.

Example 2: Creating a new Lead with email validation

This example validates an email field and creates a new lead record in the CRM.

{% extends 'DfE/Layouts/2ColumnWideLeft' %}

{% block title %}{% endblock %}

{% block main %}

  <div class="govuk-form-group">
    <h1 class="govuk-label-wrapper">
      <label class="govuk-label govuk-label--l" for="emailaddress">
        ## 'What is your email address?' set as the title in the content web page
        {{ page.title }}
      </label>
    </h1>
    <input class="govuk-input" id="emailaddress" name="emailaddress" type="email">
  </div>

  <button type="submit" id="submit-btn" class="govuk-button" data-module="govuk-button">Continue</button>

  <div id="loading-spinner" class="loader govuk-visually-hidden"></div>

  <script type="text/javascript">
    // Define the validation data object
    const inputObj = [
      {
        identifier: "emailaddress",
        type: "email",
        friendlyName: "your email address",
        required: true,
        resolve: false
      }
    ];

    // Attach event handler to the submit button
    $("#submit-btn").on("click", async function(event) {
      event.preventDefault();

      // Disable the button and show the spinner
      DfEPortal.DisableButton("submit-btn");
      DfEPortal.ShowLoadingSpinner("loading-spinner");

      try {
        // Validate the input data
        await DfEPortal.ValidateData(inputObj);

        // Construct the data object for submission
        const dataObj = await DfEPortal.WebApi.ConstructData(inputObj);

        // Create the lead record
        const entityName = "lead";
        const leadId = await DfEPortal.WebApi.Create(entityName, dataObj);

        // Redirect or notify the user of success
        alert("Lead created successfully!");
        // Optionally redirect: window.location.href = `{{ sitemarkers['NEXT_PAGE_SITEMARKER'].url }}?leadid=${leadId}`;

      } catch (error) {
        console.error("Error:", error);
        // The framework automatically displays user-friendly error messages
        // Re-enable the button and hide the spinner
        DfEPortal.EnableButton("submit-btn");
        DfEPortal.HideLoadingSpinner("loading-spinner");
      }
    });
  </script>
{% endblock %}

Explanation:

  • The inputObj sets the validation rules for the email field
  • ValidateData checks the email input ensuring it has a value and that the value is in a valid email format.
  • ConstructData prepares the email for submission to the CRM.
  • Create submits the data to the lead entity, returning the new record's ID.
  • The form is reset on success, and errors are handled with the button and spinner reset.

Example 3: Calling a cloud flow with custom error handling

This example calls a cloud flow to check if an application already exists, and displays a custom error message using the error helper functions if it does.

{% extends 'DfE/Layouts/2ColumnWideLeft' %}

{% block title %}{% endblock %}

{% block main %}

  <div id="error-summary-container"></div>

  <h1 class="govuk-heading-xl">Before you start</h1>

  <p class="govuk-body">We need to check if an application already exists for your organisation.</p>

  <button type="submit" id="check-btn" class="govuk-button" data-module="govuk-button">Check and continue</button>

  <div id="loading-spinner" class="loader govuk-visually-hidden"></div>

  <script type="text/javascript">
    // Define your cloud flow API ID and query parameters
    const flowApiId = "your-cloud-flow-api-id";
    const organisationId = "{{ user.parentcustomerid.Id }}";
    const applicationRoundId = "{{ settings['ApplicationRoundId'] }}";

    // Attach event handler to the button
    $("#check-btn").on("click", async function(event) {
      event.preventDefault();

      // Disable the button and show the spinner
      DfEPortal.DisableButton("check-btn");
      DfEPortal.ShowLoadingSpinner("loading-spinner");

      try {
        // Prepare the data to send to the cloud flow
        const queryData = {
          "OrganisationId": organisationId,
          "ApplicationRoundId": applicationRoundId
        };

        // Call the cloud flow
        const result = await DfEPortal.WebApi.CallCloudFlow(flowApiId, queryData);

        // Check if the cloud flow returned an error
        if (!result.success) {
          throw result.error;
        }

        // Check the response and show custom error if needed
        if (result.applicationexists) {
          // Use the error helper functions to display a custom error
          DfEPortal.Errors.ShowErrorSummary();
          DfEPortal.Errors.AddErrorSummaryDetail("check-btn", "An application for your organisation already exists.");
          DfEPortal.Errors.FocusErrorSummary();

          // Re-enable button and hide spinner
          DfEPortal.EnableButton("check-btn");
          DfEPortal.HideLoadingSpinner("loading-spinner");
          return;
        }

        // Success - redirect to next page
        window.location.href = "{{ sitemarkers['NEXT_PAGE_SITEMARKER'].url }}";

      } catch (error) {
        console.error("Error:", error);
        // Show a generic error message
        DfEPortal.Errors.ShowErrorSummary();
        DfEPortal.Errors.AddErrorSummaryDetail("check-btn", "There was a problem checking your application. Please try again.");
        DfEPortal.Errors.FocusErrorSummary();

        // Re-enable the button and hide the spinner
        DfEPortal.EnableButton("check-btn");
        DfEPortal.HideLoadingSpinner("loading-spinner");
      }
    });
  </script>
{% endblock %}

Explanation:

  • The queryData object contains the parameters to pass to the cloud flow.
  • CallCloudFlow sends the request to the specified cloud flow and awaits the response.
  • The response object contains properties returned by the cloud flow (e.g. success, applicationexists).
  • If the business logic check fails, custom error messages are displayed using DfEPortal.Errors.ShowErrorSummary, AddErrorSummaryDetail, and FocusErrorSummary.
  • This pattern is useful for server-side validation that cannot be performed in the browser.

Best practices

  • Use ValidateData before calling ConstructData or any Web API functions to ensure data integrity.
  • Use try-catch blocks to handle errors, and leverage the framework's error display functions (DfEPortal.Errors.ShowErrorSummary).
  • Use DisableButton and ShowLoadingSpinner during processing to prevent multiple submissions and provide visual feedback.
  • Ensure your inputObj properties (e.g. required, targetType) match the form's requirements to avoid unexpected behavior.
  • Avoid logging sensitive data (e.g. email addresses) to the console in production.