Introduction
JavaScript is one of the most powerful customization tools available in Dynamics 365 model-driven apps. Whether you're showing/hiding fields, validating data, or calling external services, client-side scripting lets you create responsive, user-friendly experiences.
This guide covers the essentials every CE consultant needs to know.
Setting Up Your Web Resource
Before writing any code, you need to create a JavaScript web resource in your solution:
- Navigate to your solution in make.powerapps.com
- Click New > More > Web Resource
- Set the type to Script (JScript)
- Use a consistent naming convention:
publisher_/scripts/entity_main.js
// account_main.js
var Contoso = Contoso || {};
Contoso.Account = Contoso.Account || {};
(function () {
"use strict";
this.onLoad = function (executionContext) {
var formContext = executionContext.getFormContext();
console.log("Account form loaded: " + formContext.data.entity.getId());
};
this.onSave = function (executionContext) {
var formContext = executionContext.getFormContext();
// Your save logic here
};
}).call(Contoso.Account);Working with Form Context
The formContext object is your gateway to everything on the form. Always retrieve it from the execution context:
this.onLoad = function (executionContext) {
var formContext = executionContext.getFormContext();
// Get attribute value
var name = formContext.getAttribute("name").getValue();
// Set attribute value
formContext.getAttribute("revenue").setValue(50000);
// Show/hide a section
formContext.ui.tabs
.get("tab_details")
.sections.get("section_financial")
.setVisible(true);
// Set field requirement level
formContext.getAttribute("emailaddress1").setRequiredLevel("required");
};Calling the Web API
Use Xrm.WebApi for CRUD operations directly from form scripts:
// Retrieve a record
Xrm.WebApi.retrieveRecord(
"account",
accountId,
"?$select=name,revenue"
).then(
function (result) {
console.log("Account: " + result.name);
console.log("Revenue: " + result.revenue);
},
function (error) {
console.error("Error: " + error.message);
}
);
// Create a record
var data = {
name: "New Account",
revenue: 75000,
"primarycontactid@odata.bind": "/contacts(" + contactId + ")",
};
Xrm.WebApi.createRecord("account", data).then(function (result) {
console.log("Created account: " + result.id);
});Common Patterns
Show/Hide Fields Based on Option Set
this.onTypeChange = function (executionContext) {
var formContext = executionContext.getFormContext();
var type = formContext.getAttribute("customertypecode").getValue();
var showCompanyFields = type === 1; // Corporate
formContext.getControl("numberofemployees").setVisible(showCompanyFields);
formContext.getControl("revenue").setVisible(showCompanyFields);
formContext.getControl("sic").setVisible(showCompanyFields);
};Add Pre-Save Validation
this.onSave = function (executionContext) {
var formContext = executionContext.getFormContext();
var email = formContext.getAttribute("emailaddress1").getValue();
var phone = formContext.getAttribute("telephone1").getValue();
if (!email && !phone) {
executionContext.getEventArgs().preventDefault();
formContext.ui.setFormNotification(
"Please provide either an email or phone number.",
"ERROR",
"contact_validation"
);
} else {
formContext.ui.clearFormNotification("contact_validation");
}
};Disable Fields Based on Status
this.lockFieldsOnClose = function (executionContext) {
var formContext = executionContext.getFormContext();
var status = formContext.getAttribute("statecode").getValue();
if (status === 1) {
// Inactive
formContext.getControl("name").setDisabled(true);
formContext.getControl("revenue").setDisabled(true);
formContext.getControl("emailaddress1").setDisabled(true);
}
};Best Practices
- Always use the execution context — never rely on
Xrm.Page(deprecated since v9). - Namespace your code to avoid conflicts with other customizations.
- Use async/await with Web API calls to keep your code clean and readable.
- Register events through the form editor, not inline in HTML web resources.
- Test in multiple browsers — Edge Chromium is the standard, but verify in Chrome and Firefox.
- Handle null values — always check if an attribute value is null before using it.
- Use form type checks — behave differently on Create vs. Update forms.
Next Steps
Now that you have the fundamentals, explore these related topics:
- TypeScript for model-driven apps — better intellisense and type safety
- PCF controls — build custom components with full framework support
- Ribbon commands — extend the command bar with custom buttons