Skip to main contentMAF Configuration Practices

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.js
this.page.state.isLoading = true;
const item = this.page.state.selectedItem;
// In XML binding
value="{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 mandatory
  • searchable - Enables searching on this field
  • default-value - Sets default value for new records
  • id - 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

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 ready
  • applicationResumed(app) - App comes to foreground

Page Events:

  • pageInitialized(page, app) - Page is created
  • pageResumed(page, app) - Page becomes active
  • pagePaused(page, app) - Page becomes inactive

Datasource Events:

  • onDatasourceInitialized(datasource, owner, app) - Datasource is ready
  • onBeforeLoadData(datasource, query) - Before data loads
  • onAfterLoadData(datasource, items, query) - After data loads
  • onValueChanged({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 opens
this.loadWorkOrderData(page);

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 controller
applicationInitialized(app) {
this.app = app;
}
// Page-level controller
pageInitialized(page, app) {
this.page = page;

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>

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'}}"/>

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

Next Steps:

Advanced Topics:

Reference:

Page last updated: 13 January 2026