UNPKG

scaffold-davaverse-client-ui

Version:

A scaffolding tool for creating Dataverse client-side web resource projects with TypeScript, ESLint, Prettier, and Rollup

284 lines (221 loc) 7.92 kB
# {{PROJECT_NAME}} A Dataverse client-side web resource project built with TypeScript. ## Project Structure This project follows a structured approach to organizing Dataverse web resource handlers: ``` src/ ├── index.ts # Main entry point - exports all handlers └── EntityName/ # Replace with your actual entity name (e.g., Account, Contact) ├── index.ts # Entity-level exports ├── OnLoad/ # Form OnLoad event handlers │ ├── index.ts # OnLoad exports │ └── HandleSomethingOnLoad.ts ├── OnChange/ # Field OnChange event handlers │ ├── index.ts # OnChange exports │ └── HandleFieldChange.ts └── OnSave/ # Form OnSave event handlers ├── index.ts # OnSave exports └── HandleFormSave.ts ``` ## Export Patterns ### 1. Individual Function Exports (Recommended) Export specific functions for each event handler: ```typescript // src/Account/OnLoad/HandleAccountLoad.ts export function handleAccountLoad(context: Xrm.Events.EventContext): void { const formContext = context.getFormContext(); // Your logic here } // src/Account/OnChange/HandleNameChange.ts export function handleAccountNameChange(context: Xrm.Events.EventContext): void { const formContext = context.getFormContext(); const nameControl = formContext.getControl("name"); // Your logic here } ``` ### 2. Namespace/Object Export Pattern Group related functions under a namespace: ```typescript // src/Account/AccountHandlers.ts export const AccountHandlers = { onLoad: { handleFormLoad: (context: Xrm.Events.EventContext) => { // Load logic }, initializeForm: (context: Xrm.Events.EventContext) => { // Initialization logic } }, onChange: { handleNameChange: (context: Xrm.Events.EventContext) => { // Name change logic }, handleStatusChange: (context: Xrm.Events.EventContext) => { // Status change logic } } }; ``` ### 3. Class-Based Pattern For complex entities with shared state: ```typescript // src/Account/AccountManager.ts export class AccountManager { private formContext: Xrm.FormContext; constructor(context: Xrm.Events.EventContext) { this.formContext = context.getFormContext(); } public handleLoad(): void { this.initializeFields(); this.setupValidation(); } public handleNameChange(): void { const name = this.formContext.getAttribute("name")?.getValue(); // Handle name change } private initializeFields(): void { // Private helper methods } } // Usage in form events: export function handleAccountLoad(context: Xrm.Events.EventContext): void { const manager = new AccountManager(context); manager.handleLoad(); } ``` ## Best Practices ### 1. Function Naming - Use descriptive names that indicate the event and purpose - Prefix with `handle` for event handlers: `handleAccountLoad`, `handleNameChange` - Use camelCase for consistency ### 2. Error Handling Always wrap your handlers in try-catch blocks: ```typescript export function handleAccountLoad(context: Xrm.Events.EventContext): void { try { const formContext = context.getFormContext(); // Your logic here } catch (error) { console.error('Error in handleAccountLoad:', error); // Optional: Show user-friendly message Xrm.Navigation.openAlertDialog({ text: "An error occurred while loading the form.", title: "Error" }); } } ``` ### 3. Type Safety Leverage TypeScript for better development experience: ```typescript interface AccountData { name: string; accountnumber: string; telephone1: string; } export function handleAccountLoad(context: Xrm.Events.EventContext): void { const formContext = context.getFormContext(); // Type-safe attribute access const nameAttr = formContext.getAttribute<string>("name"); const phoneAttr = formContext.getAttribute<string>("telephone1"); if (nameAttr && phoneAttr) { // Your logic here } } ``` ### 4. Async Operations Handle asynchronous operations properly: ```typescript export async function handleAccountLoad(context: Xrm.Events.EventContext): Promise<void> { try { const formContext = context.getFormContext(); // Async web API call const relatedData = await Xrm.WebApi.retrieveMultipleRecords( "contact", "?$filter=parentcustomerid eq " + formContext.data.entity.getId() ); // Process the data console.log(`Found ${relatedData.entities.length} related contacts`); } catch (error) { console.error('Error loading related data:', error); } } ``` ### 5. Form Context Validation Always validate form context availability: ```typescript export function handleFieldChange(context: Xrm.Events.EventContext): void { const formContext = context.getFormContext(); if (!formContext) { console.warn('Form context not available'); return; } const attribute = context.getEventSource(); if (!attribute) { console.warn('Event source not available'); return; } // Your logic here } ``` ## Dataverse Web API Examples ### Retrieve Records ```typescript // Get current record data const recordId = formContext.data.entity.getId(); const entityName = formContext.data.entity.getEntityName(); const record = await Xrm.WebApi.retrieveRecord(entityName, recordId, "?$select=name,telephone1"); ``` ### Create Records ```typescript const newContact = { firstname: "John", lastname: "Doe", emailaddress1: "john.doe@example.com" }; const result = await Xrm.WebApi.createRecord("contact", newContact); ``` ### Update Records ```typescript const updateData = { telephone1: "555-0123" }; await Xrm.WebApi.updateRecord(entityName, recordId, updateData); ``` ## Building and Deployment ### Development ```bash # Install dependencies {{PACKAGE_MANAGER_INSTALL}} # Build the project {{PACKAGE_MANAGER_BUILD}} # Lint your code {{PACKAGE_MANAGER_RUN}} lint # Format your code {{PACKAGE_MANAGER_RUN}} format ``` ### Deployment 1. Run `{{PACKAGE_MANAGER_BUILD}}` to compile TypeScript 2. The compiled JavaScript will be in the `dist/` folder 3. Upload `dist/bundle.js` as a JavaScript web resource in Dataverse 4. Configure form events to call your exported functions ## Form Event Configuration In Dataverse form designer, reference your functions like this: - **Library**: `your_webresource_name` - **Function**: `YourNamespace.handleAccountLoad` (for namespace exports) - **Function**: `handleAccountLoad` (for direct exports) ## Debugging Use browser developer tools to debug your web resources: ```typescript // Add debugging statements console.log('Form loaded:', formContext.data.entity.getEntityName()); console.debug('Current user:', Xrm.Utility.getGlobalContext().userSettings.userId); // Set breakpoints in browser dev tools debugger; // This will pause execution ``` ## Additional Resources - [Microsoft Dataverse Web API Reference](https://docs.microsoft.com/en-us/power-apps/developer/data-platform/webapi/reference/about) - [Client API Reference](https://docs.microsoft.com/en-us/power-apps/developer/model-driven-apps/clientapi/reference) - [TypeScript Handbook](https://www.typescriptlang.org/docs/) ## License MIT