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.
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
Each input object must include the following properties:
identifier
: String- The schema name of the target CRM field (e.g.,dfe_firstname
).type
: String - The type of input. Supported types are:number
,whole-number
,decimal-number
,text
,text-area
,email
,tel
,radio
,checkbox
,search
,select
,date
.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.resolve
: Boolean - Always set tofalse
. The framework sets this totrue
if validation succeeds.
Additional properties for specific input types
Depending on the input type, additional properties may be required or optional.
Text input for record search
For text inputs used to search records (e.g. finding an application by reference number):
targetType
: String - Set tolookup
.targetEntity
: String - Schema name of the table to search (e.g.,dfe_application
).targetEntityPrimaryKey
: String - Primary key schema name of the table (e.g.,dfe_applicationid
).targetEntitySearchField
: String - Field to search within the target table.
Date input to text field
For date inputs submitted to a text field (instead of a date/time field):
targetType
: String - Set totext
.
Radio inputs
For radio inputs:
targetType
: String - Supported types:lookup
,bool
,select
,text
.targetEntity
: String - Required iftargetType
islookup
. Schema name of the lookup table.
Checkbox inputs
For checkbox inputs:
targetType
: String - Supported types:bool
,select
,text
.
Select inputs
For select (dropdown) inputs:
targetType
: String - Supported types:lookup
,select
,text
.targetEntity
: String- Required iftargetType
islookup
. Schema name of the lookup table.
Available functions
The CRM Helper Framework provides core functions for validation and data submission, as well as helper functions for enhancing the user experience.
Core Functions
DfEPortal.ValidateData(inputObj)
Purpose: Validates user input data on the webpage.
Parameters:
inputObj
: Array of objects - The validation data object array (see above).
Return value: A Promise that resolves if all inputs pass validation, or rejects if any fail.
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.
DfEPortal.WebApi.ConstructData(inputsObj)
Purpose: Constructs a data object for submission to the CRM via the Power Pages WebApi.
Parameters:
inputsObj
: Array of objects - The validated input object array.
Return value: A Promise that resolves with 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 byConstructData
.
Return value: 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 byConstructData
.
Return value: A Promise that resolves with the ID of the created record 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(entityName, recordId)
Purpose: Deletes a record from the CRM.
Parameters:
entityName
: String - Schema name of the table.recordId
: String - ID of the record to delete.
Return value: 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.
Additional helper functions
DfEPortal.DisableButton(buttonID) / DfEPortal.EnableButton(buttonID)
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
).
Return value: 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(spinnerID) / DfEPortal.HideLoadingSpinner(spinnerID)
Purpose: Shows or hides a loading spinner to indicate processing.
Parameters:
spinnerID
: String - The ID of the spinner element (e.g.loading-spinner
).
Return value: 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 (sincerequired: true
).ConstructData
prepares the data for submission to the CRM.Update
submits the data to thecontact
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 thelead
entity, returning the new record's ID.- The form is reset on success, and errors are handled with the button and spinner reset.
Best practices
- Use
ValidateData
before callingConstructData
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
andShowLoadingSpinner
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.