Basic MAF Concepts
Overview
Understanding core MAF concepts is essential for effective configuration and customization. This guide covers the fundamental building blocks you’ll use throughout your MAF development journey.
Core Concepts
Datasources
Datasources are the primary mechanism for managing data in MAF applications. They provide a consistent interface for loading, manipulating, and saving data.
Key Points:
- Connect to Maximo object structures or JSON data
- Handle CRUD operations automatically
- Support offline capabilities in mobile apps
- Emit lifecycle events for custom logic
Example:
<maximo-datasource id="woListDS" object-structure="MXAPIWODETAIL"saved-query="ASSIGNEDWORK"><schema><attribute name="wonum" searchable="true" id="attr_001"/><attribute name="description" searchable="true" id="attr_002"/></schema></maximo-datasource>
Learn More: Datasources Guide
State Management
State allows you to store and manage data within pages and applications. State values are reactive - when they change, the UI automatically updates.
Types of State:
- Page State - Scoped to a specific page
- Application State - Available throughout the application
Example:
<page id="workorder"><states><!-- Simple state --><state name="isLoading" value="false" type="boolean" id="state_001"/><!-- Object state --><state name="selectedItem" type="object" id="state_002"/><!-- Array state -->
Accessing State:
// In AppCustomizations.jsthis.page.state.isLoading = true;const item = this.page.state.selectedItem;// In XML bindingvalue="{page.state.isLoading}"
Attributes
Attributes define the properties of data in datasources and control behavior of UI components.
Datasource Attributes
Define the structure and behavior of data fields:
<schema><!-- Basic attribute --><attribute name="wonum" type="STRING" id="attr_001"/><!-- Searchable attribute --><attribute name="description" searchable="true" id="attr_002"/><!-- Required attribute --><attribute name="status" required="true" id="attr_003"/>
Common Attribute Properties:
name- Field name (required)type- Data type (STRING, NUMBER, BOOLEAN, DATE, etc.)required- Marks field as mandatorysearchable- Enables searching on this fielddefault-value- Sets default value for new recordsid- Unique identifier (required)
UI Component Attributes
Control component behavior and appearance:
<!-- Required field --><text-input value="{item.wonum}" required="true" id="input_001"/><!-- Read-only field --><text-input value="{item.status}" readonly="true" id="input_002"/><!-- Disabled field --><text-input value="{item.description}" disabled="{page.state.isLocked}" id="input_003"/>
Required Fields
The required attribute is used extensively to enforce data validation.
At Datasource Level
When an attribute is marked as required in the datasource schema, all UI fields bound to it automatically inherit this requirement:
<schema><attribute name="description" type="STRING" required="true" id="attr_001"/><attribute name="location" type="STRING" required="true" id="attr_002"/></schema>
At UI Level
You can also mark individual UI components as required:
<text-input value="{page.state.name}"required="true"invalid="{page.state.nameInvalid}"invalid-text="{page.state.nameInvalidText}"id="input_001"/>
Validation Behavior:
- Asterisk (*) appears next to field label
- Form submission is prevented if field is empty
- Custom error messages can be displayed
Learn More: Marking Required Fields
Event Lifecycle
MAF components emit events at various stages of their lifecycle. You can hook into these events to add custom logic.
Common Lifecycle Events
Application Events:
applicationInitialized(app)- App is readyapplicationResumed(app)- App comes to foreground
Page Events:
pageInitialized(page, app)- Page is createdpageResumed(page, app)- Page becomes activepagePaused(page, app)- Page becomes inactive
Datasource Events:
onDatasourceInitialized(datasource, owner, app)- Datasource is readyonBeforeLoadData(datasource, query)- Before data loadsonAfterLoadData(datasource, items, query)- After data loadsonValueChanged({datasource, item, field, oldValue, newValue})- Field value changes
Example:
class AppCustomizations {applicationInitialized(app) {this.app = app;}pageResumed(page, app) {if (page.name === 'workorder') {// Custom logic when workorder page opensthis.loadWorkOrderData(page);
Learn More: Datasources Guide - Lifecycle Events
Bindings
Bindings connect data to UI components using curly brace syntax {expression}.
Types of Bindings:
<!-- Datasource binding --><text-input value="{item.wonum}" id="input_001"/><!-- State binding --><text-input value="{page.state.searchText}" id="input_002"/><!-- Conditional binding --><button disabled="{!page.state.isValid}" id="btn_001"/>
Two-Way Binding: Input components automatically update the bound data when users make changes.
Controllers
Controllers contain the business logic for pages, dialogs, and datasources. They handle events and implement custom behavior.
Built-in Controllers: MAF applications come with pre-built controllers that handle standard functionality.
Custom Controllers: You can add custom logic through AppCustomizations.js:
class AppCustomizations {// Application-level controllerapplicationInitialized(app) {this.app = app;}// Page-level controllerpageInitialized(page, app) {this.page = page;
Learn More: Replace Existing Method
Lookups
Lookups provide a searchable list for selecting values. They’re commonly used for reference data like locations, assets, or status codes.
Basic Lookup:
<!-- Define lookup datasource --><maximo-datasource id="locationLookupDS"lookup-data="true"object-structure="MXAPILOCATION"selection-mode="single"><schema><attribute name="location" searchable="true" id="attr_001"/><attribute name="description" searchable="true" id="attr_002"/></schema>
Learn More: Create WO Priority Lookup
Queries
Queries filter datasource data. There are several types:
Saved Query: Pre-defined query in Maximo object structure:
<maximo-datasource id="woListDS" saved-query="ASSIGNEDWORK"/>
Where Clause: SQL-like filter for server-side filtering:
<maximo-datasource id="woListDS" where="status='INPRG'"/>
QBE (Query By Example): Programmatic filtering:
await datasource.initializeQbe();datasource.setQBE('status', '=', 'INPRG');await datasource.searchQBE();
Mobile QBE Filter: Client-side filtering for mobile apps:
<maximo-datasource id="woListDS"mobile-qbe-filter="{{'status': 'INPRG'}}"/>
Learn More: Modify Default Queries
Common Patterns
Conditional Rendering
Show/hide elements based on conditions:
<!-- Show only if condition is true --><button label="Complete"hidden="{item.status === 'COMP'}"id="btn_001"/><!-- Show based on state --><container hidden="{!page.state.showDetails}" id="container_001"><!-- Content --></container>
Dynamic Values
Set values dynamically based on logic:
<!-- Dynamic label --><button label="{item.status === 'COMP' ? 'Reopen' : 'Complete'}"id="btn_001"/><!-- Dynamic styling --><text value="{item.priority}"color="{item.priority > 3 ? 'red' : 'black'}"id="text_001"/>
Event Handling
Respond to user interactions:
<!-- Button click --><button label="Save"on-click="saveWorkOrder"on-click-arg="{item}"id="btn_001"/><!-- Field change --><text-input value="{item.description}"on-change="handleDescriptionChange"
Best Practices
✅ Do
- Use descriptive IDs for all elements
- Leverage datasource-level validation when possible
- Use state for temporary UI data
- Bind directly to datasource items for persistent data
- Add comments to complex bindings
- Use lifecycle events for initialization logic
❌ Don’t
- Manipulate DOM directly
- Store large objects in state unnecessarily
- Create circular dependencies in bindings
- Ignore error handling in async operations
- Hardcode values that should be configurable
Related Resources
Next Steps:
- Datasources Guide - Deep dive into datasources
- Making Fields Read-Only - Field configuration
- Marking Required Fields - Validation patterns
Advanced Topics:
- Replace Existing Method - Controller customization
- Additional Logic - Extending functionality
Reference: