Files
gh-secondsky-sap-skills-ski…/references/data-binding-models.md
2025-11-30 08:55:36 +08:00

19 KiB

SAPUI5 Data Binding & Models

Source: Official SAP SAPUI5 Documentation Documentation: https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials Last Updated: 2025-11-21


Data Binding Overview

Data binding connects UI controls to data sources, automatically synchronizing changes bidirectionally.

Key Benefits:

  • Automatic UI updates when data changes
  • Reduced boilerplate code
  • Clean separation of data and presentation
  • Type conversion and formatting
  • Validation support

Binding Types:

  1. Property Binding: Single value (text, enabled, visible)
  2. Aggregation Binding: Collections (table items, list items)
  3. Element Binding: Object context
  4. Expression Binding: Inline calculations

Documentation: https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials (search: binding, data-binding)


Binding Modes

One-Way Binding:

  • Data flows model → view only
  • UI updates when model changes
  • User input doesn't update model
  • Use for read-only data
// In manifest.json
"models": {
    "": {
        "dataSource": "mainService",
        "settings": {
            "defaultBindingMode": "OneWay"
        }
    }
}

Two-Way Binding:

  • Data flows model ↔ view bidirectionally
  • Model updates when user changes input
  • View updates when model changes
  • Use for editable forms
"defaultBindingMode": "TwoWay"

One-Time Binding:

  • Data loaded once at initialization
  • No updates after initial load
  • Best performance for static data
<Text text="{path: '/title', mode: 'OneTime'}"/>

Documentation: https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials (search: binding-mode)


Model Types

JSON Model

Client-side model for JavaScript objects. Best for small datasets and local data.

Creation:

// In controller
var oModel = new JSONModel({
    products: [
        { id: 1, name: "Product 1", price: 100 },
        { id: 2, name: "Product 2", price: 200 }
    ],
    selectedProduct: null
});

this.getView().setModel(oModel);

From File:

var oModel = new JSONModel();
oModel.loadData("model/data.json");
this.getView().setModel(oModel);

Usage:

<List items="{/products}">
    <StandardListItem
        title="{name}"
        description="Price: {price}"/>
</List>

Key Methods:

  • setData(oData): Set complete data
  • setProperty(sPath, oValue): Set single property
  • getProperty(sPath): Get property value
  • loadData(sURL): Load from URL

Documentation: https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials (search: json-model)


OData V2 Model

Server-side model for OData v2 services. Automatic CRUD operations.

Creation:

// In manifest.json
{
    "sap.app": {
        "dataSources": {
            "mainService": {
                "uri": "/sap/opu/odata/sap/SERVICE_SRV/",
                "type": "OData",
                "settings": {
                    "odataVersion": "2.0",
                    "localUri": "localService/metadata.xml"
                }
            }
        }
    },
    "sap.ui5": {
        "models": {
            "": {
                "dataSource": "mainService",
                "settings": {
                    "defaultBindingMode": "TwoWay",
                    "defaultCountMode": "Inline",
                    "useBatch": true
                }
            }
        }
    }
}

Reading Data:

// Simple read
this.getView().getModel().read("/Products", {
    success: function(oData) {
        console.log(oData);
    },
    error: function(oError) {
        MessageBox.error("Failed to load data");
    }
});

// With filters and sorters
this.getView().getModel().read("/Products", {
    filters: [new Filter("Price", FilterOperator.GT, 100)],
    sorters: [new Sorter("Name", false)],
    urlParameters: {
        "$expand": "Category"
    },
    success: function(oData) {
        console.log(oData);
    }
});

Creating Entries:

var oModel = this.getView().getModel();
oModel.create("/Products", {
    Name: "New Product",
    Price: 150,
    CategoryID: 1
}, {
    success: function() {
        MessageToast.show("Product created");
    },
    error: function(oError) {
        MessageBox.error("Failed to create product");
    }
});

Updating Entries:

var oModel = this.getView().getModel();
oModel.update("/Products(1)", {
    Price: 200
}, {
    success: function() {
        MessageToast.show("Product updated");
    }
});

Deleting Entries:

oModel.remove("/Products(1)", {
    success: function() {
        MessageToast.show("Product deleted");
    }
});

Batch Requests:

oModel.setUseBatch(true);
oModel.setDeferredGroups(["myGroup"]);

// Add to batch
oModel.create("/Products", oData, { groupId: "myGroup" });
oModel.create("/Products", oData2, { groupId: "myGroup" });

// Submit batch
oModel.submitChanges({
    groupId: "myGroup",
    success: function() {
        MessageToast.show("Batch successful");
    }
});

Key Settings:

  • useBatch: Enable batch requests
  • defaultCountMode: How to get counts (Inline, Request, None)
  • refreshAfterChange: Auto-refresh after updates
  • defaultBindingMode: One-way or two-way

Documentation: https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials (search: odata-v2-model)


OData V4 Model

Modern OData v4 model with improved performance and features.

Creation:

// In manifest.json
{
    "sap.app": {
        "dataSources": {
            "mainService": {
                "uri": "/sap/opu/odata4/sap/service/srvd/sap/api/0001/",
                "type": "OData",
                "settings": {
                    "odataVersion": "4.0"
                }
            }
        }
    },
    "sap.ui5": {
        "models": {
            "": {
                "dataSource": "mainService",
                "settings": {
                    "synchronizationMode": "None",
                    "operationMode": "Server",
                    "autoExpandSelect": true,
                    "earlyRequests": true
                }
            }
        }
    }
}

Key Differences from V2:

  • Server-side operations (filter, sort, page)
  • Automatic $expand and $select
  • Better performance
  • Stricter adherence to OData standard
  • No client-side models

Reading with List Binding:

<Table items="{
    path: '/Products',
    parameters: {
        $expand: 'Category',
        $select: 'ID,Name,Price',
        $filter: 'Price gt 100',
        $orderby: 'Name'
    }
}">

Creating Entries:

var oListBinding = this.byId("table").getBinding("items");
var oContext = oListBinding.create({
    Name: "New Product",
    Price: 150
});

// Save
oContext.created().then(function() {
    MessageToast.show("Product created");
});

Updating:

var oContext = this.byId("table").getSelectedItem().getBindingContext();
oContext.setProperty("Price", 200);

// Save changes
oContext.getModel().submitBatch("$auto").then(function() {
    MessageToast.show("Updated");
});

Deleting:

var oContext = this.byId("table").getSelectedItem().getBindingContext();
oContext.delete("$auto").then(function() {
    MessageToast.show("Deleted");
});

Documentation: https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials (search: odata-v4-model)


Resource Model (i18n)

Model for internationalization texts.

Setup:

// In manifest.json
{
    "sap.ui5": {
        "models": {
            "i18n": {
                "type": "sap.ui.model.resource.ResourceModel",
                "settings": {
                    "bundleName": "com.mycompany.myapp.i18n.i18n",
                    "supportedLocales": ["en", "de", "fr"],
                    "fallbackLocale": "en"
                }
            }
        }
    }
}

i18n.properties:

appTitle=My Application
appDescription=A sample SAPUI5 application

# Buttons
btnSave=Save
btnCancel=Cancel
btnDelete=Delete

# Messages
msgSaveSuccess=Data saved successfully
msgDeleteConfirm=Do you want to delete this item?

# Placeholders with parameters
msgItemCount=You have {0} items selected
msgWelcome=Welcome, {0}!

Usage in XML:

<Page title="{i18n>appTitle}">
    <Button text="{i18n>btnSave}" press=".onSave"/>
    <Text text="{i18n>appDescription}"/>
</Page>

Usage in Controller:

var oBundle = this.getView().getModel("i18n").getResourceBundle();
var sTitle = oBundle.getText("appTitle");

// With parameters
var sMessage = oBundle.getText("msgItemCount", [5]);
MessageBox.success(sMessage);

Locale Files:

  • i18n.properties: Default (fallback)
  • i18n_de.properties: German
  • i18n_en.properties: English
  • i18n_fr.properties: French

Documentation: https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials (search: resource-model, i18n)


XML Model

Client-side model for XML data structures.

Creation:

var oModel = new XMLModel();
oModel.loadData("model/data.xml");
this.getView().setModel(oModel, "xml");

Usage:

<List items="{xml>/products/product}">
    <StandardListItem
        title="{xml>name}"
        description="{xml>price}"/>
</List>

Use Cases:

  • Legacy XML data sources
  • Configuration files
  • Small datasets

Documentation: https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials (search: xml-model)


Binding Syntax

Property Binding

Simple Binding:

<Text text="{/companyName}"/>
<Input value="{/employeeName}"/>

Named Models:

<Text text="{invoice>/company/name}"/>
<Input value="{customer>/email}"/>

Binding Options:

<Text text="{
    path: '/price',
    type: 'sap.ui.model.type.Currency',
    formatOptions: {
        showMeasure: false
    },
    constraints: {
        minimum: 0
    }
}"/>

Composite Binding:

<Text text="{
    parts: [
        {path: '/firstName'},
        {path: '/lastName'}
    ],
    formatter: '.formatFullName'
}"/>

Controller:

formatFullName: function(sFirstName, sLastName) {
    return sFirstName + " " + sLastName;
}

Aggregation Binding

List Binding:

<List items="{/products}">
    <StandardListItem
        title="{name}"
        description="{description}"
        info="{price} EUR"/>
</List>

Table Binding:

<Table items="{
    path: '/products',
    sorter: {
        path: 'name'
    },
    filters: {
        path: 'price',
        operator: 'GT',
        value1: 50
    }
}">
    <columns>
        <Column><Text text="Name"/></Column>
        <Column><Text text="Price"/></Column>
    </columns>
    <items>
        <ColumnListItem>
            <cells>
                <Text text="{name}"/>
                <Text text="{price}"/>
            </cells>
        </ColumnListItem>
    </items>
</Table>

With Parameters:

<Table items="{
    path: '/Products',
    parameters: {
        expand: 'Category',
        select: 'ID,Name,Price'
    }
}">

Element Binding

Sets binding context for entire control:

<Panel binding="{/selectedProduct}">
    <VBox>
        <Text text="{name}"/>
        <Text text="{description}"/>
        <Text text="{price} EUR"/>
    </VBox>
</Panel>
// In controller
onProductSelect: function(oEvent) {
    var oItem = oEvent.getParameter("listItem");
    var sPath = oItem.getBindingContext().getPath();

    this.byId("detailPanel").bindElement({
        path: sPath,
        parameters: {
            expand: "Category"
        }
    });
}

Expression Binding

Inline calculations without formatter:

<!-- Conditional text color -->
<Text
    text="{price}"
    color="{= ${price} > 100 ? 'red' : 'green'}"/>

<!-- Conditional visibility -->
<Button
    visible="{= ${status} === 'approved'}"
    text="Process"/>

<!-- Calculations -->
<Text text="{= ${quantity} * ${price}}"/>

<!-- String operations -->
<Text text="{= ${firstName} + ' ' + ${lastName}}"/>

<!-- Comparisons -->
<Button enabled="{= ${quantity} > 0 &amp;&amp; ${stock} >= ${quantity}}"/>

Supported Operations:

  • Arithmetic: +, -, *, /, %
  • Comparison: ===, !==, >, <, >=, <=
  • Logical: &&, ||, !
  • Ternary: condition ? true : false
  • String concatenation: +

Limitations:

  • Simple expressions only
  • No function calls
  • Use formatters for complex logic

Formatters & Data Types

Custom Formatters

Definition:

// In controller or separate formatter.js
formatPrice: function(sPrice) {
    if (!sPrice) return "";
    return parseFloat(sPrice).toFixed(2) + " EUR";
},

formatStatus: function(sStatus) {
    var mStatusText = {
        "A": "Approved",
        "R": "Rejected",
        "P": "Pending"
    };
    return mStatusText[sStatus] || sStatus;
},

formatDate: function(oDate) {
    if (!oDate) return "";
    var oDateFormat = sap.ui.core.format.DateFormat.getDateInstance({
        pattern: "dd.MM.yyyy"
    });
    return oDateFormat.format(oDate);
}

Usage:

<Text text="{path: 'price', formatter: '.formatPrice'}"/>
<Text text="{path: 'status', formatter: '.formatStatus'}"/>
<Text text="{path: 'createdAt', formatter: '.formatDate'}"/>

Multiple Parameters:

<Text text="{
    parts: ['quantity', 'price'],
    formatter: '.formatTotal'
}"/>
formatTotal: function(iQuantity, fPrice) {
    return (iQuantity * fPrice).toFixed(2) + " EUR";
}

Built-in Data Types

String Type:

<Input value="{
    path: '/name',
    type: 'sap.ui.model.type.String',
    constraints: {
        maxLength: 50,
        minLength: 2
    }
}"/>

Integer Type:

<Input value="{
    path: '/quantity',
    type: 'sap.ui.model.type.Integer',
    constraints: {
        minimum: 1,
        maximum: 999
    }
}"/>

Float Type:

<Input value="{
    path: '/price',
    type: 'sap.ui.model.type.Float',
    constraints: {
        minimum: 0,
        maximum: 99999.99
    },
    formatOptions: {
        minFractionDigits: 2,
        maxFractionDigits: 2
    }
}"/>

Date Type:

<DatePicker value="{
    path: '/orderDate',
    type: 'sap.ui.model.type.Date',
    formatOptions: {
        pattern: 'dd.MM.yyyy'
    }
}"/>

DateTime Type:

<DateTimePicker value="{
    path: '/createdAt',
    type: 'sap.ui.model.type.DateTime',
    formatOptions: {
        pattern: 'dd.MM.yyyy HH:mm:ss'
    }
}"/>

Currency Type:

<Text text="{
    parts: [
        {path: 'price'},
        {path: 'currency'}
    ],
    type: 'sap.ui.model.type.Currency',
    formatOptions: {
        showMeasure: true
    }
}"/>

Boolean Type:

<CheckBox selected="{
    path: '/isActive',
    type: 'sap.ui.model.type.Boolean'
}"/>

Filters & Sorters

Filters

Simple Filter:

var oFilter = new Filter("price", FilterOperator.GT, 100);
var oBinding = this.byId("table").getBinding("items");
oBinding.filter([oFilter]);

Multiple Filters (AND):

var aFilters = [
    new Filter("price", FilterOperator.GT, 100),
    new Filter("category", FilterOperator.EQ, "Electronics")
];
oBinding.filter(aFilters); // AND condition

Multiple Filters (OR):

var aFilters = [
    new Filter("status", FilterOperator.EQ, "Approved"),
    new Filter("status", FilterOperator.EQ, "Pending")
];
var oCombinedFilter = new Filter({
    filters: aFilters,
    and: false // OR condition
});
oBinding.filter([oCombinedFilter]);

Complex Filters:

var oPriceFilter = new Filter({
    filters: [
        new Filter("price", FilterOperator.GT, 100),
        new Filter("price", FilterOperator.LT, 500)
    ],
    and: true
});

var oStatusFilter = new Filter({
    filters: [
        new Filter("status", FilterOperator.EQ, "A"),
        new Filter("status", FilterOperator.EQ, "P")
    ],
    and: false
});

var oCombinedFilter = new Filter({
    filters: [oPriceFilter, oStatusFilter],
    and: true
});

oBinding.filter([oCombinedFilter]);

Filter Operators:

  • EQ: Equals
  • NE: Not equals
  • GT: Greater than
  • GE: Greater or equal
  • LT: Less than
  • LE: Less or equal
  • Contains: Contains text
  • StartsWith: Starts with text
  • EndsWith: Ends with text
  • BT: Between (requires value1 and value2)

Custom Filter Function:

var oFilter = new Filter({
    path: "price",
    test: function(oValue) {
        return oValue > 100 && oValue < 500;
    }
});

Sorters

Simple Sort:

var oSorter = new Sorter("name", false); // false = ascending
var oBinding = this.byId("table").getBinding("items");
oBinding.sort(oSorter);

Multiple Sorters:

var aSorters = [
    new Sorter("category", false),
    new Sorter("price", true) // true = descending
];
oBinding.sort(aSorters);

Sort with Grouping:

var oSorter = new Sorter("category", false, true); // third param = group
oBinding.sort(oSorter);

Custom Group Function:

var oSorter = new Sorter("price", false, function(oContext) {
    var fPrice = oContext.getProperty("price");
    if (fPrice < 100) return { key: "low", text: "Low Price" };
    if (fPrice < 500) return { key: "medium", text: "Medium Price" };
    return { key: "high", text: "High Price" };
});


Note: This document covers data binding and model usage in SAPUI5. For specific implementation details and advanced scenarios, refer to the official documentation links provided.