4.1 The Client API Object Model & JavaScript Form Scripting

Key Takeaways

  • formContext, retrieved via executionContext.getFormContext(), replaces the deprecated single-form Xrm.Page reference in all new client scripting.
  • Attributes hold field data (getValue/setValue, requirement level, dirty state); controls govern the rendered UI (visibility, enabled state, focus) for that same field.
  • setValue() only changes the in-memory value; the record is not persisted to Dataverse until the form saves.
  • Disabling or hiding a control changes UI behavior only and does not enforce platform-level security by itself.
  • Form notifications use a developer-supplied uniqueId so they can be targeted and cleared with clearFormNotification later.
Last updated: July 2026

When you write JavaScript to customize a model-driven app form, you are not scripting the raw DOM — you are scripting against the Client API object model, a structured set of objects that Microsoft Dataverse exposes to every form. Getting comfortable with this object model, and specifically with the modern formContext pattern, is foundational to nearly every scenario the "Extend the user experience" domain tests.

Xrm, formContext, and executionContext

The global Xrm object is the root of the Client API. Historically, developers reached into Xrm.Page to read and write form data, but Xrm.Page is a deprecated, global, single-form reference that breaks in multi-form scenarios — for example, when a form opens inside a dialog on top of another form, or when a quick-create panel and a main form are open at the same time. PL-400 expects you to know the modern replacement: every event handler receives an executionContext as its first parameter, and you call executionContext.getFormContext() to retrieve a formContext object scoped to that specific form instance.

function onLoadHandler(executionContext) {
    const formContext = executionContext.getFormContext();
    const statusAttr = formContext.getAttribute("statuscode");
    console.log(statusAttr.getValue());
}

Always design new libraries around formContext, not Xrm.Page — the exam rewards recognizing Xrm.Page usage in a code sample as a legacy pattern to flag or replace.

The Pillars of formContext

formContext exposes namespaces you will use constantly:

  • formContext.data — the record's data, including data.entity (the current record: getId(), getEntityName(), save()) and data.entity.attributes (the collection of field attributes).
  • formContext.ui — visual/UI elements: tabs, sections, form-level notifications, and the current form's view state.
  • formContext.getAttribute(name) / formContext.getControl(name) — the two most-used accessors. An attribute is the underlying data for a field (value, requirement level, dirty state); a control is the rendered UI for that field on this specific form (visibility, enabled state, focus).

That attribute-versus-control split matters: two different form types can both expose the same attribute, but each has its own control, so visibility and enable-state changes must target the control, while value changes target the attribute.

Reading and Writing Attribute Values

Every field on a form has a corresponding attribute object reachable through formContext.getAttribute("schemaname"):

MethodPurpose
getValue()Returns the current typed value (string, number, Date, lookup array, or option-set integer)
setValue(value)Sets the value in memory; does not by itself fire OnChange handlers
getRequiredLevel() / setRequiredLevel()Reads/sets none, recommended, or required
getIsDirty()True if the value changed since load or the last save
fireOnChange()Manually triggers any registered OnChange handlers for that attribute

Lookup fields return an array of objects with id, name, and entityType; option sets return the numeric option value, not the display label. A common exam trap is assuming setValue() automatically persists the change — it does not; the record is only saved to Dataverse when the form saves, whether by user action or formContext.data.save().

Controlling Controls: Visibility, Enable State, and Focus

Controls govern what the user sees, independent of the underlying data:

formContext.getControl("telephone1").setVisible(false);
formContext.getControl("emailaddress1").setDisabled(true);
formContext.getControl("fullname").setFocus();

Hiding a control does not clear its value, and disabling a control does not enforce platform-level security — a user with edit privileges could still change the value through the Web API, a Power Automate flow, or another client. Security-sensitive field locking belongs in security roles or field-level security, not client scripting alone. This distinction between client-side UI behavior and platform-enforced security is a recurring PL-400 theme that connects this section back to the technical design domain.

Form and Field Notifications

Two levels of notification exist. Form-level notifications use formContext.ui.setFormNotification(message, level, uniqueId), where level is "ERROR", "WARNING", or "INFO", and uniqueId lets you later clear that specific message with formContext.ui.clearFormNotification(uniqueId). Field-level notifications use formContext.getControl(name).setNotification(message, uniqueId) to place a validation icon next to a single field. Because uniqueId is developer-supplied rather than auto-generated, forgetting to track and clear it is a common source of "stuck" notifications that persist after the underlying issue has been resolved.

Practical Guidance for the Exam

Expect scenario questions that give you a requirement — for example, "show a warning if the credit limit exceeds $50,000 but still let the user save the record" — and ask you to identify the correct object-model call. Keep these anchors in mind:

  • formContext.getAttribute() always precedes value access; you cannot call getValue() directly on formContext itself.
  • Client API code only runs in the browser session where it executes; it is never a substitute for server-side validation enforced by plug-ins or business rules when true data integrity is required.
  • The Client API reference groups objects under Xrm.Page (deprecated), formContext, Xrm.Utility, Xrm.Navigation, Xrm.WebApi, and Xrm.Device — each with a distinct responsibility that the rest of this chapter builds on, from event registration to the Web API and PCF components that must interoperate with this same form data.

Mastering formContext as the single entry point for reading, writing, and displaying form data is the prerequisite for everything else in this chapter.

Test Your Knowledge

Which Client API pattern should a new PL-400 form-scripting library use to retrieve the current form's data context, instead of the deprecated single-form Xrm.Page reference?

A
B
C
D
Test Your Knowledge

A developer needs to keep the Email field's value visible on a form but prevent the user from editing it through the UI. Which formContext call accomplishes this?

A
B
C
D