Domain 4 β€” Module 1 of 4 25%
11 of 26 overall
Domain 4: Extend the User Experience Free ⏱ ~14 min read

Client Scripting: Form Events & the Client API

Add intelligence to model-driven app forms with JavaScript. Learn the Client API object model, form event handlers, and how to call the Dataverse Web API from client scripts.

Adding brains to model-driven forms

Simple explanation

Think of a smart home system.

Without automation, you manually turn lights on, adjust the thermostat, and lock the doors. With a smart system, things happen automatically: motion sensor triggers lights, temperature drops trigger heating, and the door locks when you leave.

Client scripts are the smart system for model-driven app forms. When a user opens a form (OnLoad), changes a field (OnChange), or saves a record (OnSave) β€” your JavaScript responds automatically. You can show or hide fields, set values, validate data, display notifications, and even call external APIs.

The Client API object model

The Client API gives you access to everything on the form:

ObjectAccess ViaWhat It Controls
formContextexecutionContext.getFormContext()The form itself β€” all controls, fields, tabs
formContext.data.entityForm dataField values, save state, entity name
formContext.uiForm UITabs, sections, controls, notifications
formContext.getAttribute(β€œname”)Specific fieldGet/set value, add onChange handler
formContext.getControl(β€œname”)Specific controlShow/hide, enable/disable, set label
Xrm.NavigationGlobalOpen forms, dialogs, web resources
Xrm.WebApiGlobalCRUD operations on Dataverse tables
Xrm.UtilityGlobalShow progress indicator, get entity metadata

The formContext pattern

Every event handler receives an execution context. From that, you get the form context:

// Modern pattern β€” ALWAYS use this
function onFormLoad(executionContext) {
    var formContext = executionContext.getFormContext();
    
    // Read a field value
    var priority = formContext.getAttribute("priority").getValue();
    
    // Show/hide a control
    if (priority === 1) {
        formContext.getControl("escalation_notes").setVisible(true);
    }
    
    // Set a field as required
    formContext.getAttribute("description").setRequiredLevel("required");
    
    // Display a notification
    formContext.ui.setFormNotification(
        "This is a high-priority case", "WARNING", "priority_warning"
    );
}
Exam tip: Xrm.Page is deprecated

If you see Xrm.Page.getAttribute(...) in an exam answer, it is almost certainly wrong. The modern approach is:

  1. Receive executionContext as a parameter
  2. Call executionContext.getFormContext() to get formContext
  3. Use formContext for all form operations

Xrm.Page still works (backwards compatibility) but is deprecated and does not support multi-session apps where multiple forms can be open simultaneously.

Event handler registration

Event handlers connect your JavaScript functions to form events. There are two ways to register them:

Register in the form designer unless you have a specific reason not to
MethodForm Designer (Recommended)Programmatic (addOnChange)
HowConfigure in the form editor UI under Events tabCall formContext.getAttribute('field').addOnChange(handler) in code
Visible to makersYes β€” shows in form propertiesNo β€” hidden in JavaScript
Solution-awareYes β€” travels with the form customisationDepends on when/where the code runs
Best forOnLoad, OnSave, field OnChange events you always wantDynamic handlers added conditionally at runtime
Exam preferenceAlmost always correctCorrect only when dynamic registration is needed

Calling the Dataverse Web API from scripts

The Xrm.WebApi object lets you perform CRUD operations from client scripts without leaving the form:

// Read a related record
function loadAccountDetails(formContext) {
    var accountRef = formContext.getAttribute("parentaccountid").getValue()[0];
    var accountId = accountRef.id.replace(/[{}]/g, ""); // Strip braces from GUID
    
    Xrm.WebApi.retrieveRecord("account", accountId, 
        "?$select=name,revenue,numberofemployees"
    ).then(
        function(result) {
            // Use the data
            formContext.getControl("account_info").setLabel(
                result.name + " - Revenue: $" + result.revenue
            );
        },
        function(error) {
            console.error("Error: " + error.message);
        }
    );
}

// Create a related record
function createFollowUpTask(formContext) {
    var caseId = formContext.data.entity.getId();
    var record = {
        subject: "Follow up on case",
        "regardingobjectid_incident@odata.bind": "/incidents(" + caseId + ")",
        scheduledend: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) // 7 days from now
    };
    
    Xrm.WebApi.createRecord("task", record).then(
        function(result) {
            formContext.ui.setFormNotification(
                "Follow-up task created", "INFO", "task_created"
            );
        },
        function(error) {
            formContext.ui.setFormNotification(
                "Error creating task: " + error.message, "ERROR", "task_error"
            );
        }
    );
}
Scenario: Kai adds smart form behaviour

Kai is building a shipment form at LogiFlow. When the destination country changes:

  1. OnChange handler fires on the destinationcountry field
  2. Script calls Xrm.WebApi.retrieveMultipleRecords() to fetch customs requirements for that country
  3. If the country requires customs documentation, the script shows the β€œCustoms Documents” tab and sets the β€œCustoms Declaration” field as required
  4. If the country is domestic, the script hides the tab and clears the requirement

This logic is registered through the form designer (OnChange on destinationcountry) and uses formContext for all form interactions. The Web API call is async β€” the form remains responsive while data loads.

Question

What is the execution context in client scripting?

Click or press Enter to reveal answer

Answer

The execution context is an object passed to every event handler function. It contains metadata about the event (which field changed, save mode, etc.) and provides getFormContext() to access the form. Always pass execution context as the first parameter and check 'Pass execution context' in the form designer.

Click to flip back

Question

How do you show a notification on a model-driven app form using JavaScript?

Click or press Enter to reveal answer

Answer

Use formContext.ui.setFormNotification(message, level, uniqueId). Levels: 'ERROR' (red), 'WARNING' (yellow), 'INFO' (blue). The uniqueId lets you clear the notification later with formContext.ui.clearFormNotification(uniqueId).

Click to flip back

Question

What is the difference between getAttribute and getControl in the Client API?

Click or press Enter to reveal answer

Answer

getAttribute('name') returns the data attribute β€” use it to get/set field values, add OnChange handlers, and check required level. getControl('name') returns the UI control β€” use it to show/hide, enable/disable, set labels, and manage focus. Same field, different access points.

Click to flip back

Knowledge Check

Kai registers an OnLoad handler on a model-driven form. The handler should read the Priority field and conditionally show a tab. Which code pattern is correct?

Next up: Commands, Buttons & Custom Page Navigation β€” customising the command bar with Power Fx and JavaScript.