Estendere l’esperienza utente con il client scripting

Nelle model-driven app di Power Apps puoi arricchire il comportamento dei form scrivendo JavaScript che reagisce agli eventi del form. Non è un rimpiazzo delle regole business dichiarative (business rules), ma la scelta corretta quando la logica è troppo complessa, condizionale o dinamica per essere espressa senza codice: nascondere/mostrare sezioni, popolare campi da lookup, validare prima del salvataggio, chiamare la Web API di Dataverse. Il codice si aggancia all’event framework e viene distribuito come JavaScript web resource.

L’event framework dei form

Gli event handler si registrano su eventi specifici del form; a runtime la piattaforma li richiama nell’ordine di registrazione.

  • OnLoad — scatta quando il form viene caricato o ricaricato. È il punto giusto per registrare handler dinamici (es. addOnChange) e per impostare lo stato iniziale dell’UI.
  • OnSave — scatta al salvataggio. Qui puoi validare e, se necessario, annullare il salvataggio con executionContext.getEventArgs().preventDefault(). Ricorda che può essere innescato anche da auto-save.
  • OnChange — scatta quando il valore di un column/attribute cambia e il campo perde il focus. Nota: impostare un valore via codice non fa scattare l’OnChange, a meno di chiamare fireOnChange().
  • TabStateChange — scatta quando una tab passa da expanded a collapsed (o viceversa). Utile per caricare dati “on demand” solo quando l’utente apre una tab, migliorando le performance di caricamento.
  • OnStage / OnStageChange / OnStageSelected — riguardano il Business Process Flow: si attivano quando cambia lo stage attivo o quando uno stage viene selezionato, permettendo logica legata all’avanzamento del processo.

executionContext e formContext

Ogni handler deve ricevere l’executionContext come primo parametro. Da esso ottieni il formContext, che è l’oggetto autoritativo per interagire con il form corrente:

function onLoad(executionContext) {
  const formContext = executionContext.getFormContext();
  formContext.getAttribute("name").addOnChange(onNameChange);
}
  • executionContext dà accesso agli event args (es. preventDefault) e al getFormContext().
  • formContext espone getAttribute() (i dati) e getControl() (i controlli UI), sostituendo il vecchio oggetto globale Xrm.Page.

Distinzione chiave a livello associate: getAttribute() opera sul dato (valore, requiredLevel, submitMode), getControl() opera sul controllo visuale (visibilità, disabilitazione, notifiche). Un attributo può avere più controlli sullo stesso form.

Deploy come JavaScript web resource

Il codice va caricato come web resource di tipo Script (JScript), preferibilmente dentro una solution per la portabilità tra ambienti. Poi, nel form designer, si registra l’handler:

  1. si aggiunge la library (la web resource) al form;
  2. si associa la function all’evento desiderato, indicando il nome esatto della funzione;
  3. si spunta “Pass execution context as first parameter”.

Senza quella spunta, la funzione non riceve l’executionContext e ogni getFormContext() fallisce.

Best practice

  • Codice asincrono: usa async/await o le Promise ritornate dalla Web API; evita chiamate sincrone che bloccano l’UI. In OnSave puoi restituire una Promise per operazioni async prima del commit.
  • Solo API documentate e supportate: usa il Client API reference (Xrm.*, formContext.*). Manipolare direttamente il DOM o usare API non documentate è non supportato e si rompe agli update.
  • Registrazione mirata: registra gli handler su eventi specifici e usa addOnChange/removeOnChange per gestire il ciclo di vita, evitando duplicazioni al ricaricamento del form.
  • Difensività: verifica che attributi/controlli esistano (possono mancare in form diversi) prima di usarli.

Trappole tipiche d’esame

  • Scenario: la tua funzione chiama Xrm.Page.getAttribute(...)Sbagliato. Xrm.Page è deprecato; usa executionContext.getFormContext() e opera sul formContext.
  • Scenario: l’handler va in errore “cannot read getFormContext of undefined” → hai dimenticato di spuntare “Pass execution context as first parameter” nella registrazione dell’evento.
  • Scenario: devi caricare dati pesanti solo quando l’utente espande una sezione a tab → registra un handler su TabStateChange, non su OnLoad.
  • Scenario: devi impedire il salvataggio se una validazione fallisce → in OnSave chiama executionContext.getEventArgs().preventDefault(), non lanciare un’eccezione generica.
  • Scenario: imposti un valore via setValue() e ti aspetti che parta la logica OnChange → non scatta automaticamente; chiama esplicitamente fireOnChange() sull’attributo.