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 algetFormContext(). - formContext espone
getAttribute()(i dati) egetControl()(i controlli UI), sostituendo il vecchio oggetto globaleXrm.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:
- si aggiunge la library (la web resource) al form;
- si associa la function all’evento desiderato, indicando il nome esatto della funzione;
- 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/awaito 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/removeOnChangeper 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; usaexecutionContext.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 esplicitamentefireOnChange()sull’attributo.