Skip to content
Back to articles
JavaScript / TypeScriptFeatured

Getting Started with Model-Driven App JavaScript

A practical guide to writing JavaScript for Dynamics 365 model-driven apps, covering form events, web API calls, and common patterns.

February 10, 202610 min
Share

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:

  1. Navigate to your solution in make.powerapps.com
  2. Click New > More > Web Resource
  3. Set the type to Script (JScript)
  4. 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

Tags

#javascript#model-driven-apps#form-scripting#dynamics-365

Get new articles in your inbox

No spam. Unsubscribe anytime.

Related articles

You might also find these helpful