@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
1,007 lines (974 loc) • 117 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2009-2023 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
/**
* Model and related classes like bindings for OData V4.
*
* <b>Note:</b> Smart controls (<code>sap.ui.comp</code> library) do not support the SAPUI5 OData V4
* model. Also controls such as {@link sap.ui.table.TreeTable} and
* {@link sap.ui.table.AnalyticalTable} are not supported together with the SAPUI5 OData V4 model.
* The interface for applications has been changed for easier and more efficient use of the model.
* For a summary of these changes, see
* {@link topic:abd4d7c7548d4c29ab8364d3904a6d74 Changes Compared to OData V2 Model}.
*
* @name sap.ui.model.odata.v4
* @namespace
* @public
* @since 1.37.0
*/
//Provides class sap.ui.model.odata.v4.ODataModel
sap.ui.define([
"./ODataContextBinding",
"./ODataListBinding",
"./ODataMetaModel",
"./ODataPropertyBinding",
"./SubmitMode",
"./lib/_GroupLock",
"./lib/_Helper",
"./lib/_MetadataRequestor",
"./lib/_Parser",
"./lib/_Requestor",
"sap/base/assert",
"sap/base/Log",
"sap/ui/base/SyncPromise",
"sap/ui/core/cache/CacheManager",
"sap/ui/core/Configuration",
"sap/ui/core/library",
"sap/ui/core/message/Message",
"sap/ui/model/BindingMode",
"sap/ui/model/Context",
"sap/ui/model/Model",
"sap/ui/model/odata/OperationMode",
"sap/ui/thirdparty/URI"
], function (ODataContextBinding, ODataListBinding, ODataMetaModel, ODataPropertyBinding,
SubmitMode, _GroupLock, _Helper, _MetadataRequestor, _Parser, _Requestor, assert, Log,
SyncPromise, CacheManager, Configuration, coreLibrary, Message, BindingMode, BaseContext,
Model, OperationMode, URI) {
"use strict";
var sClassName = "sap.ui.model.odata.v4.ODataModel",
// system query options allowed within a $expand query option
aExpandQueryOptions = ["$count", "$expand", "$filter", "$levels", "$orderby", "$search",
"$select"],
// binding-specific parameters allowed in getKeepAliveContext
aGetKeepAliveParameters = ["$$groupId", "$$patchWithoutSideEffects", "$$updateGroupId"],
MessageType = coreLibrary.MessageType,
aMessageTypes = [
undefined,
MessageType.Success,
MessageType.Information,
MessageType.Warning,
MessageType.Error
],
mSupportedEvents = {
dataReceived : true,
dataRequested : true,
messageChange : true,
propertyChange : true,
sessionTimeout : true
},
mSupportedParameters = {
annotationURI : true,
autoExpandSelect : true,
earlyRequests : true,
groupId : true,
groupProperties : true,
httpHeaders : true,
ignoreAnnotationsFromMetadata : true,
metadataUrlParams : true,
odataVersion : true,
operationMode : true,
serviceUrl : true,
sharedRequests : true,
supportReferences : true,
synchronizationMode : true,
updateGroupId : true
},
// system query options allowed in mParameters
aSystemQueryOptions = ["$apply", "$count", "$expand", "$filter", "$orderby", "$search",
"$select"],
// valid header values: non-empty, only US-ASCII, no control chars
rValidHeader = /^[ -~]+$/,
/**
* Constructor for a new ODataModel.
*
* @param {object} mParameters
* The parameters
* @param {string|string[]} [mParameters.annotationURI]
* The URL (or an array of URLs) from which the annotation metadata are loaded.
* The annotation files are merged into the service metadata in the given order (last one
* wins). The same annotations are overwritten; if an annotation file contains other
* elements (like a type definition) that are already merged, an error is thrown.
* Supported since 1.41.0
* @param {boolean} [mParameters.autoExpandSelect]
* Whether the OData model's bindings automatically generate $select and $expand system
* query options from the binding hierarchy. Note: Dynamic changes to the binding
* hierarchy are not supported. This parameter is supported since 1.47.0, and since 1.75.0
* it also enables property paths containing navigation properties in
* <code>$select</code>.
* @param {boolean} [mParameters.earlyRequests]
* Whether the following is requested at the earliest convenience:
* <ul>
* <li> root $metadata document and annotation files;
* <li> the security token.
* </ul>
* Note: The root $metadata document and annotation files are just requested but not yet
* converted from XML to JSON unless really needed.
* Supported since 1.53.0.
* <b>BEWARE:</b> The default value may change to <code>true</code> in later releases.
* You may also set {@link topic:26ba6a5c1e5c417f8b21cce1411dba2c Manifest Model Preload}
* in order to further speed up the start of a UI5 component.
* @param {string} [mParameters.groupId="$auto"]
* Controls the model's use of batch requests: '$auto' bundles requests from the model in
* a batch request which is sent automatically before rendering; '$direct' sends requests
* directly without batch; other values result in an error
* @param {object} [mParameters.groupProperties]
* Controls the use of batch requests for application groups. A map of application
* group IDs having an object with exactly one property <code>submit</code>. Valid values
* are 'API', 'Auto', 'Direct' see {@link sap.ui.model.odata.v4.SubmitMode}. Supported
* since 1.51.0
* @param {object} [mParameters.httpHeaders]
* Map of HTTP header names to their values, see {@link #changeHttpHeaders}
* @param {boolean} [mParameters.ignoreAnnotationsFromMetadata]
* Whether to ignore all annotations from service metadata and "cross-service references";
* only the value <code>true</code> is allowed. Only annotations from annotation files
* (see parameter "annotationURI") are loaded. This parameter is not inherited by value
* list models. @experimental as of version 1.111.0
* @param {object} [mParameters.metadataUrlParams]
* Additional map of URL parameters used specifically for $metadata requests. Note that
* "sap-context-token" applies only to the service's root $metadata, but not to
* "cross-service references". Supported since 1.81.0
* @param {string} [mParameters.odataVersion="4.0"]
* The version of the OData service. Supported values are "2.0" and "4.0".
* @param {sap.ui.model.odata.OperationMode} [mParameters.operationMode]
* The operation mode for filtering and sorting. Since 1.39.0, the operation mode
* {@link sap.ui.model.odata.OperationMode.Server} is supported. All other operation modes
* including <code>undefined</code> lead to an error if 'vFilters' or 'vSorters' are given
* or if {@link sap.ui.model.odata.v4.ODataListBinding#filter} or
* {@link sap.ui.model.odata.v4.ODataListBinding#sort} is called.
* @param {string} mParameters.serviceUrl
* Root URL of the service to request data from. The path part of the URL must end with a
* forward slash according to OData V4 specification ABNF, rule "serviceRoot". You may
* append OData custom query options to the service root URL separated with a "?", for
* example "/MyService/?custom=foo". See specification <a href=
* "https://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part2-url-conventions.html#_Custom_Query_Options"
* >"OData Version 4.0 Part 2: URL Conventions", "5.2 Custom Query Options"</a>. OData
* system query options and OData parameter aliases lead to an error.
* @param {boolean} [mParameters.sharedRequests]
* Whether all list bindings for the same resource path share their data, so that it is
* requested only once; only the value <code>true</code> is allowed; see parameter
* "$$sharedRequest" of {@link #bindList}. Additionally,
* {@link sap.ui.model.BindingMode.OneWay} becomes the default binding mode and
* {@link sap.ui.model.BindingMode.TwoWay} is forbidden. Note: This makes all bindings
* read-only, so it may be especially useful for value list models. Supported since 1.80.0
* @param {boolean} [mParameters.supportReferences=true]
* Whether <code><edmx:Reference></code> and <code><edmx:Include></code> directives
* are supported in order to load schemas on demand from other $metadata documents and
* include them into the current service ("cross-service references").
* @param {string} [mParameters.synchronizationMode]
* (Controls synchronization between different bindings which refer to the same data for
* the case data changes in one binding. Must be set to 'None' which means bindings are
* not synchronized at all; all other values are not supported and lead to an error.)
* <b>deprecated:</b> As of version 1.110.0, this parameter is optional; see also
* {@link topic:648e360fa22d46248ca783dc6eb44531 Data Reuse}
* @param {string} [mParameters.updateGroupId]
* The group ID that is used for update requests. If no update group ID is specified,
* <code>mParameters.groupId</code> is used. Valid update group IDs are
* <code>undefined</code>, '$auto', '$direct' or an application group ID.
* @throws {Error} If an unsupported synchronization mode is given, if the given service
* root URL does not end with a forward slash, if an unsupported parameter is given, if
* OData system query options or parameter aliases are specified as parameters, if an
* invalid group ID or update group ID is given, if the given operation mode is not
* supported, if an annotation file cannot be merged into the service metadata, if an
* unsupported value for <code>odataVersion</code> is given.
*
* @alias sap.ui.model.odata.v4.ODataModel
* @author SAP SE
* @class Model implementation for OData V4.
*
* This model is not prepared to be inherited from.
*
* Every resource path (relative to the service root URL, no query options) according to
* <a href=
* "https://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part2-url-conventions.html#resource-pathurl4"
* >"4 Resource Path"</a> in specification "OData Version 4.0 Part 2: URL Conventions" is
* a valid data binding path within this model if a leading slash is added; for example
* "/" + "SalesOrderList('A%2FB%26C')" to access an entity instance with key "A/B&C". Note
* that appropriate URI encoding is necessary, see the example of
* {@link sap.ui.model.odata.v4.ODataUtils.formatLiteral}. "4.5.1 Addressing Actions"
* needs an operation binding, see {@link sap.ui.model.odata.v4.ODataContextBinding}.
*
* Note that the OData V4 model has its own {@link sap.ui.model.odata.v4.Context} class.
* Bindings which are relative to such a V4 context depend on their corresponding parent
* binding and do not access data with their own service requests unless parameters are
* provided.
*
* <b>Group IDs</b> control the model's use of batch requests. Valid group IDs are:
* <ul>
* <li> <b>$auto</b> and <b>$auto.*</b>: Bundles requests from the model in a batch
* request which is sent automatically before rendering. You can use different
* '$auto.*' group IDs to use different batch requests. The suffix may be any
* non-empty string consisting of alphanumeric characters from the basic Latin
* alphabet, including the underscore. The submit mode for these group IDs is always
* {@link sap.ui.model.odata.v4.SubmitMode#Auto}.
* <li> <b>$direct</b>: Sends requests directly without batch. The submit mode for this
* group ID is always {@link sap.ui.model.odata.v4.SubmitMode#Direct}.
* <li> An application group ID, which is a non-empty string consisting of alphanumeric
* characters from the basic Latin alphabet, including the underscore. By default, an
* application group has the submit mode {@link sap.ui.model.odata.v4.SubmitMode#API}.
* It is possible to use a different submit mode; for details see
* <code>mParameters.groupProperties</code>.
* </ul>
*
* @extends sap.ui.model.Model
* @public
* @since 1.37.0
* @version 1.111.5
*/
ODataModel = Model.extend("sap.ui.model.odata.v4.ODataModel",
/** @lends sap.ui.model.odata.v4.ODataModel.prototype */{
constructor : constructor
});
//*********************************************************************************************
// ODataModel
//*********************************************************************************************
/**
* Constructor for a new ODataModel.
*
* @param {object} mParameters
* The parameters
* @throws {Error} If an unsupported synchronization mode is given, if the given service root
* URL does not end with a forward slash, if an unsupported parameter is given, if OData
* system query options or parameter aliases are specified as parameters, if an invalid group
* ID or update group ID is given, if the given operation mode is not supported, if an
* annotation file cannot be merged into the service metadata, if an unsupported value for
* <code>odataVersion</code> is given.
*/
function constructor(mParameters) {
var sGroupId,
oGroupProperties,
sLanguageTag = Configuration.getLanguageTag(),
sODataVersion,
sParameter,
mQueryParams,
sServiceUrl,
oUri,
mUriParameters,
that = this;
// do not pass any parameters to Model
Model.call(this);
mParameters = mParameters || {};
if ("synchronizationMode" in mParameters && mParameters.synchronizationMode !== "None") {
throw new Error("Synchronization mode must be 'None'");
}
sODataVersion = mParameters.odataVersion || "4.0";
this.sODataVersion = sODataVersion;
if (sODataVersion !== "4.0" && sODataVersion !== "2.0") {
throw new Error("Unsupported value for parameter odataVersion: " + sODataVersion);
}
for (sParameter in mParameters) {
if (!(sParameter in mSupportedParameters)) {
throw new Error("Unsupported parameter: " + sParameter);
}
}
sServiceUrl = mParameters.serviceUrl;
if (!sServiceUrl) {
throw new Error("Missing service root URL");
}
oUri = new URI(sServiceUrl);
if (oUri.path()[oUri.path().length - 1] !== "/") {
throw new Error("Service root URL must end with '/'");
}
if (mParameters.operationMode && mParameters.operationMode !== OperationMode.Server) {
throw new Error("Unsupported operation mode: "
+ mParameters.operationMode);
}
this.sOperationMode = mParameters.operationMode;
// Note: strict checking for model's URI parameters, but "sap-*" is allowed
mUriParameters = this.buildQueryOptions(oUri.query(true), false, true);
// BEWARE: these are shared across all bindings!
this.mUriParameters = mUriParameters;
if (Configuration.getStatisticsEnabled()) {
// Note: this way, "sap-statistics" is not sent within $batch
mUriParameters = Object.assign({"sap-statistics" : true}, mUriParameters);
}
this.sServiceUrl = oUri.query("").toString();
this.sGroupId = mParameters.groupId;
if (this.sGroupId === undefined) {
this.sGroupId = "$auto";
}
if (this.sGroupId !== "$auto" && this.sGroupId !== "$direct") {
throw new Error("Group ID must be '$auto' or '$direct'");
}
_Helper.checkGroupId(mParameters.updateGroupId, false, "Invalid update group ID: ");
this.sUpdateGroupId = mParameters.updateGroupId || this.getGroupId();
this.mGroupProperties = {};
for (sGroupId in mParameters.groupProperties) {
_Helper.checkGroupId(sGroupId, true);
oGroupProperties = mParameters.groupProperties[sGroupId];
if (typeof oGroupProperties !== "object"
|| Object.keys(oGroupProperties).length !== 1
|| !(oGroupProperties.submit in SubmitMode)) {
throw new Error("Group '" + sGroupId + "' has invalid properties: '"
+ oGroupProperties + "'");
}
}
this.mGroupProperties = _Helper.clone(mParameters.groupProperties) || {};
this.mGroupProperties.$auto = {submit : SubmitMode.Auto};
this.mGroupProperties.$direct = {submit : SubmitMode.Direct};
if (mParameters.autoExpandSelect !== undefined
&& typeof mParameters.autoExpandSelect !== "boolean") {
throw new Error("Value for autoExpandSelect must be true or false");
}
this.bAutoExpandSelect = mParameters.autoExpandSelect === true;
if ("sharedRequests" in mParameters && mParameters.sharedRequests !== true) {
throw new Error("Value for sharedRequests must be true");
}
this.bSharedRequests = mParameters.sharedRequests === true;
this.bIgnoreETag = false;
if ("ignoreAnnotationsFromMetadata" in mParameters
&& mParameters.ignoreAnnotationsFromMetadata !== true) {
throw new Error("Value for ignoreAnnotationsFromMetadata must be true");
}
// BEWARE: do not share mHeaders between _MetadataRequestor and _Requestor!
this.mHeaders = {"Accept-Language" : sLanguageTag};
this.mMetadataHeaders = {"Accept-Language" : sLanguageTag};
mQueryParams = Object.assign({}, mUriParameters, mParameters.metadataUrlParams);
this.oMetaModel = new ODataMetaModel(
_MetadataRequestor.create(this.mMetadataHeaders, sODataVersion,
mParameters.ignoreAnnotationsFromMetadata, mQueryParams),
this.sServiceUrl + "$metadata", mParameters.annotationURI, this,
mParameters.supportReferences, mQueryParams["sap-language"]);
this.oInterface = {
fetchEntityContainer : this.oMetaModel.fetchEntityContainer.bind(this.oMetaModel),
fetchMetadata : this.oMetaModel.fetchObject.bind(this.oMetaModel),
fireMessageChange : this.fireMessageChange.bind(this),
fireDataReceived : this.fireDataReceived.bind(this),
fireDataRequested : this.fireDataRequested.bind(this),
fireSessionTimeout : function () {
that.fireEvent("sessionTimeout");
},
getGroupProperty : this.getGroupProperty.bind(this),
getMessagesByPath : this.getMessagesByPath.bind(this),
getOptimisticBatchEnabler : this.getOptimisticBatchEnabler.bind(this),
getReporter : this.getReporter.bind(this),
isIgnoreETag : function () {
return that.bIgnoreETag;
},
onCreateGroup : function (sGroupId) {
if (that.isAutoGroup(sGroupId)) {
that.addPrerenderingTask(that._submitBatch.bind(that, sGroupId, true));
}
},
reportStateMessages : this.reportStateMessages.bind(this),
reportTransitionMessages : this.reportTransitionMessages.bind(this)
};
this.oRequestor = _Requestor.create(this.sServiceUrl, this.oInterface, this.mHeaders,
mUriParameters, sODataVersion);
this.changeHttpHeaders(mParameters.httpHeaders);
this.bEarlyRequests = mParameters.earlyRequests;
if (this.bEarlyRequests) {
this.oMetaModel.fetchEntityContainer(true);
this.initializeSecurityToken();
this.oRequestor.sendOptimisticBatch();
}
this.aAllBindings = [];
// The bindings holding keep-alive contexts without a $$getKeepAlive binding
this.mKeepAliveBindingsByPath = {};
this.mSupportedBindingModes = {
OneTime : true,
OneWay : true
};
if (mParameters.sharedRequests) {
this.sDefaultBindingMode = BindingMode.OneWay;
} else {
this.sDefaultBindingMode = BindingMode.TwoWay;
this.mSupportedBindingModes.TwoWay = true;
}
this.aPrerenderingTasks = null; // @see #addPrerenderingTask
this.fnOptimisticBatchEnabler = null;
// maps the path to the error for the next dataReceived event
this.mPath2DataReceivedError = {};
// maps a path to the difference between fireDataRequested and fireDataReceived calls, to
// ensure the events are respectively fired once for a GET request
this.mPath2DataRequestedCount = {};
}
/**
* Submits the requests associated with this group ID in one batch request.
*
* @param {string} sGroupId
* The group ID
* @param {boolean} [bCatch]
* Whether the returned promise always resolves and never rejects
* @returns {sap.ui.base.SyncPromise}
* A promise on the outcome of the HTTP request resolving with <code>undefined</code>; it is
* rejected with an error if the batch request itself fails. Use <code>bCatch</code> to catch
* that error and make the promise resolve with <code>undefined</code> instead.
*
* @private
*/
ODataModel.prototype._submitBatch = function (sGroupId, bCatch) {
var that = this;
return this.oRequestor.submitBatch(sGroupId).catch(function (oError) {
that.reportError("$batch failed", sClassName, oError);
if (!bCatch) {
throw oError;
}
});
};
/**
* The 'dataReceived' event is fired when the back-end data has been received on the client. It
* is only fired for GET requests and is to be used by applications to process an error. For
* each 'dataRequested' event, a 'dataReceived' event is fired.
*
* If a back-end request is successful, the event has almost no parameters. For compatibility
* with {@link sap.ui.model.Binding#event:dataReceived 'dataReceived'}, an event parameter
* <code>data : {}</code> is provided: "In error cases it will be undefined", but otherwise it
* is not. For additional property requests, the absolute path to the entity is also available.
*
* The 'dataReceived' event can be triggered by a binding or by additional property requests for
* an entity that already has been requested. Events triggered by a binding may be bubbled up to
* the model, while events triggered by additional property requests are fired directly by the
* model.
*
* If a back-end request fails, the 'dataReceived' event provides an <code>Error</code> in the
* 'error' event parameter. If multiple requests are processed within a single $batch
* (or even a single change set), the order of 'dataReceived' events is not guaranteed. For
* requests which are not processed because a previous request failed, <code>error.cause</code>
* points to the root cause error - you should either ignore those events, or unwrap the error
* to access the root cause immediately. For additional property requests, the absolute path to
* the entity is also available.
*
* @param {sap.ui.base.Event} oEvent
* @param {object} oEvent.getParameters()
* @param {object} [oEvent.getParameters().data]
* An empty data object if a back-end request succeeds
* @param {Error} [oEvent.getParameters().error]
* The error object if a back-end request failed.
* @param {string} [oEvent.getParameters().path]
* The absolute path to the entity which caused the event. The path is only provided for
* additional property requests; for other requests it is <code>undefined</code>.
*
* @event sap.ui.model.odata.v4.ODataModel#dataReceived
* @public
* @see sap.ui.model.odata.v4.ODataContextBinding#event:dataReceived
* @see sap.ui.model.odata.v4.ODataListBinding#event:dataReceived
* @see sap.ui.model.odata.v4.ODataModel#event:dataRequested
* @since 1.106.0
*/
/**
* The 'dataRequested' event is fired directly after data has been requested from a back end.
* It is only fired for GET requests. For each 'dataRequested' event, a 'dataReceived' event is
* fired.
*
* For additional property requests, the absolute path to the entity is available as an event
* parameter.
*
* The 'dataRequested' event can be triggered by a binding or by additional property requests
* for an entity that already has been requested.
* Events triggered by a binding may be bubbled up to the model, while events triggered by
* additional property requests are fired directly by the model. Every GET request caused by
* additional properties is causing one 'dataRequested' event.
*
* There are two kinds of requests leading to such an event:
* <ul>
* <li> When a binding requests initial data, or a list binding requests data for additional
* rows, the event is fired at the binding and may be bubbled up to the model. This includes
* refreshes except those triggered by
* {@link sap.ui.model.odata.v4.Context#requestSideEffects}.
* <li> For additional property requests for an entity that already has been requested, the
* event is only fired at the model.
* </ul>
*
* @param {sap.ui.base.Event} oEvent
* @param {object} oEvent.getParameters()
* @param {string} [oEvent.getParameters().path]
* The absolute path to the entity which caused the event. The path is only provided for
* additional property requests; for other requests it is <code>undefined</code>.
*
* @event sap.ui.model.odata.v4.ODataModel#dataRequested
* @public
* @see sap.ui.model.odata.v4.ODataContextBinding#event:dataRequested
* @see sap.ui.model.odata.v4.ODataListBinding#event:dataRequested
* @see sap.ui.model.odata.v4.ODataModel#event:dataReceived
* @since 1.106.0
*/
/**
* The 'parseError' event is not supported by this model.
*
* @event sap.ui.model.odata.v4.ODataModel#parseError
* @public
* @since 1.37.0
*/
/**
* The <code>propertyChange</code> event is fired whenever one of this model's property bindings
* successfully {@link sap.ui.model.odata.v4.ODataPropertyBinding#setValue changes its value}
* due to {@link sap.ui.model.BindingMode.TwoWay two-way} data binding. This does not apply to
* {@link sap.ui.model.odata.v4.Context#setProperty} which represents controller code changes,
* not user input.
*
* @param {sap.ui.base.Event} oEvent
* @param {object} oEvent.getParameters()
* @param {sap.ui.model.Context} [oEvent.getParameters().context]
* The property binding's {@link sap.ui.model.Binding#getContext context}, if available
* @param {string} oEvent.getParameters().path
* The property binding's {@link sap.ui.model.Binding#getPath path}
* @param {Promise} [oEvent.getParameters().promise]
* A promise on the outcome of the PATCH request, much like
* {@link sap.ui.model.odata.v4.Context#setProperty} provides it for
* <code>bRetry === true</code>; missing in case there is no PATCH
* @param {sap.ui.model.ChangeReason} oEvent.getParameters().reason
* The reason for the property change: always <code>sap.ui.model.ChangeReason.Binding</code>
* @param {string} oEvent.getParameters().resolvedPath
* The property binding's {@link sap.ui.model.Binding#getResolvedPath resolved path}
* @param {any} oEvent.getParameters().value
* The property binding's new
* {@link sap.ui.model.odata.v4.ODataPropertyBinding#getValue value}
*
* @event sap.ui.model.odata.v4.ODataModel#propertyChange
* @public
* @see sap.ui.model.Model#propertyChange
* @since 1.110.0
*/
/**
* The 'requestCompleted' event is not supported by this model.
*
* @event sap.ui.model.odata.v4.ODataModel#requestCompleted
* @public
* @since 1.37.0
*/
/**
* The 'requestFailed' event is not supported by this model.
*
* @event sap.ui.model.odata.v4.ODataModel#requestFailed
* @public
* @since 1.37.0
*/
/**
* The 'requestSent' event is not supported by this model.
*
* @event sap.ui.model.odata.v4.ODataModel#requestSent
* @public
* @since 1.37.0
*/
/**
* The 'sessionTimeout' event is fired when the server has created a session for the model and
* this session ran into a timeout due to inactivity.
*
* @event sap.ui.model.odata.v4.ODataModel#sessionTimeout
* @public
* @since 1.66.0
*/
/**
* Adds a task that is guaranteed to run once, just before the next rendering without triggering
* a rendering request. A watchdog ensures that the task is executed soon, even if no rendering
* occurs.
*
* @param {function} fnPrerenderingTask
* A function that is called before the rendering
* @param {boolean} [bFirst]
* Whether the task should become the first one, not the last one
* @private
*/
ODataModel.prototype.addPrerenderingTask = function (fnPrerenderingTask, bFirst) {
var fnRunTasks, iTimeoutId,
that = this;
function runTasks(aTasks) {
clearTimeout(iTimeoutId);
while (aTasks.length) {
aTasks.shift()();
}
if (that.aPrerenderingTasks === aTasks) {
that.aPrerenderingTasks = null;
}
}
if (!this.aPrerenderingTasks) {
this.aPrerenderingTasks = [];
fnRunTasks = runTasks.bind(null, this.aPrerenderingTasks);
sap.ui.getCore().addPrerenderingTask(fnRunTasks);
// Add a watchdog to run the tasks in case there is no rendering. Ensure that the task
// runs after all setTimeout(0) tasks scheduled from within the current task, even those
// that were scheduled afterwards. A simple setTimeout(n) with n > 0 is not sufficient
// because this doesn't help if the current task runs very long.
iTimeoutId = setTimeout(function () {
iTimeoutId = setTimeout(fnRunTasks, 0);
}, 0);
}
if (bFirst) {
this.aPrerenderingTasks.unshift(fnPrerenderingTask);
} else {
this.aPrerenderingTasks.push(fnPrerenderingTask);
}
};
/**
* Attach event handler <code>fnFunction</code> to the 'dataReceived' event of this binding.
*
* @param {function} fnFunction The function to call when the event occurs
* @param {object} [oListener] Object on which to call the given function
* @returns {this} <code>this</code> to allow method chaining
*
* @public
* @since 1.106.0
*/
ODataModel.prototype.attachDataReceived = function (fnFunction, oListener) {
return this.attachEvent("dataReceived", fnFunction, oListener);
};
/**
* Attach event handler <code>fnFunction</code> to the 'dataRequested' event of this binding.
*
* @param {function} fnFunction The function to call when the event occurs
* @param {object} [oListener] Object on which to call the given function
* @returns {this} <code>this</code> to allow method chaining
*
* @public
* @since 1.106.0
*/
ODataModel.prototype.attachDataRequested = function (fnFunction, oListener) {
return this.attachEvent("dataRequested", fnFunction, oListener);
};
/**
* See {@link sap.ui.base.EventProvider#attachEvent}
*
* @param {string} sEventId The identifier of the event to listen for
* @param {object} [_oData]
* @param {function} [_fnFunction]
* @param {object} [_oListener]
* @returns {this} <code>this</code> to allow method chaining
*
* @public
* @see sap.ui.base.EventProvider#attachEvent
* @since 1.37.0
*/
// @override sap.ui.base.EventProvider#attachEvent
ODataModel.prototype.attachEvent = function (sEventId, _oData, _fnFunction, _oListener) {
if (!(sEventId in mSupportedEvents)) {
throw new Error("Unsupported event '" + sEventId + "': v4.ODataModel#attachEvent");
}
return Model.prototype.attachEvent.apply(this, arguments);
};
/**
* Attach event handler <code>fnFunction</code> to the 'sessionTimeout' event of this model.
*
* @param {function} fnFunction The function to call when the event occurs
* @param {object} [oListener] Object on which to call the given function
* @returns {this} <code>this</code> to allow method chaining
*
* @public
* @since 1.66.0
*/
ODataModel.prototype.attachSessionTimeout = function (fnFunction, oListener) {
return this.attachEvent("sessionTimeout", fnFunction, oListener);
};
/**
* Creates a new context binding for the given path, context and parameters.
*
* @param {string} sPath
* The binding path in the model; must not end with a slash
* @param {sap.ui.model.odata.v4.Context} [oContext]
* The context which is required as base for a relative path
* @param {object} [mParameters]
* Map of binding parameters which can be OData query options as specified in <a href=
* "https://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part2-url-conventions.html"
* >"OData Version 4.0 Part 2: URL Conventions"</a> or the binding-specific parameters as
* specified below.
* Note: The binding creates its own data service request if it is absolute or if it has any
* parameters or if it is relative and has a context created via
* {@link #createBindingContext}.
* The following OData query options are allowed:
* <ul>
* <li> All "5.2 Custom Query Options" except for those with a name starting with "sap-"
* (unless starting with "sap-valid-")
* <li> The $count, $expand, $filter, $levels, $orderby, $search and $select
* "5.1 System Query Options"; OData V4 only allows $count, $filter, $levels, $orderby and
* $search inside resource paths that identify a collection. In our case here, this means
* you can only use them inside $expand.
* </ul>
* All other query options lead to an error.
* Query options specified for the binding overwrite model query options.
* @param {string|object} [mParameters.$expand]
* The value for the "5.1.2 System Query Option $expand" or an object which determines that
* value. The object is a map from expand path to expand options, where the options are again
* maps of system query options, typically with string values. $count can also be given as a
* <code>boolean</code> value, $expand can recursively be given as a map, $levels can also be
* given as a <code>number</code> value, and $select can also be given as an array (but
* without navigation paths). An empty map can also be given as <code>null</code> or
* <code>true</code>. See also {@link topic:1ab4f62de6ab467096a2a98b363a1373 Parameters}.
* @param {string|string[]} [mParameters.$select]
* A comma separated list or an array of items which determine the value for the
* "5.1.3 System Query Option $select". Since 1.75.0, when using the "autoExpandSelect" model
* parameter (see {@link sap.ui.model.odata.v4.ODataModel#constructor}), paths with navigation
* properties can be included and will contribute to the "5.1.2 System Query Option $expand".
* @param {boolean} [mParameters.$$canonicalPath]
* Whether a binding relative to an {@link sap.ui.model.odata.v4.Context} uses the canonical
* path computed from its context's path for data service requests; only the value
* <code>true</code> is allowed.
* @param {string} [mParameters.$$groupId]
* The group ID to be used for <b>read</b> requests triggered by this binding; if not
* specified, either the parent binding's group ID (if the binding is relative) or the
* model's group ID is used, see {@link sap.ui.model.odata.v4.ODataModel#constructor}.
* Valid values are <code>undefined</code>, '$auto', '$auto.*', '$direct' or application group
* IDs as specified in {@link sap.ui.model.odata.v4.ODataModel}.
* @param {boolean} [mParameters.$$inheritExpandSelect]
* For operation bindings only: Whether $expand and $select from the parent binding are used
* in the request sent on {@link #execute}. If set to <code>true</code>, the binding must not
* set the $expand itself, the operation must be bound, and the return value and the binding
* parameter must belong to the same entity set.
* @param {boolean} [mParameters.$$ownRequest]
* Whether the binding always uses an own service request to read its data; only the value
* <code>true</code> is allowed.
* @param {boolean} [mParameters.$$patchWithoutSideEffects]
* Whether implicit loading of side effects via PATCH requests is switched off; only the value
* <code>true</code> is allowed. This sets the preference "return=minimal" and requires the
* service to return an ETag header for "204 No Content" responses. If not specified, the
* value of the parent binding is used.
* @param {string} [mParameters.$$updateGroupId]
* The group ID to be used for <b>update</b> requests triggered by this binding;
* if not specified, either the parent binding's update group ID (if the binding is relative)
* or the model's update group ID is used, see
* {@link sap.ui.model.odata.v4.ODataModel#constructor}.
* For valid values, see parameter "$$groupId".
* @returns {sap.ui.model.odata.v4.ODataContextBinding}
* The context binding
* @throws {Error}
* If disallowed binding parameters are provided, for example if the binding parameter
* $$inheritExpandSelect is set to <code>true</code> and the binding is no operation binding
* or the binding has the parameter $expand.
*
* @public
* @see sap.ui.model.Model#bindContext
* @since 1.37.0
*/
// @override sap.ui.model.Model#bindContext
ODataModel.prototype.bindContext = function (sPath, oContext, mParameters) {
return new ODataContextBinding(this, sPath, oContext, mParameters);
};
/**
* Callback function for all V4 bindings to add themselves to their model.
*
* @param {sap.ui.model.odata.v4.ODataContextBinding|sap.ui.model.odata.v4.ODataListBinding|sap.ui.model.odata.v4.ODataPropertyBinding} oBinding
* A context, list, or property binding
*
* @private
*/
ODataModel.prototype.bindingCreated = function (oBinding) {
this.aAllBindings.push(oBinding);
};
/**
* Callback function for all V4 bindings to remove themselves from their model.
*
* @param {sap.ui.model.odata.v4.ODataContextBinding|sap.ui.model.odata.v4.ODataListBinding|sap.ui.model.odata.v4.ODataPropertyBinding} oBinding
* A context, list, or property binding
* @throws {Error}
* If a binding is removed twice or without adding.
*
* @private
*/
ODataModel.prototype.bindingDestroyed = function (oBinding) {
var iIndex = this.aAllBindings.indexOf(oBinding);
if (iIndex < 0) {
throw new Error("Unknown " + oBinding);
}
this.aAllBindings.splice(iIndex, 1);
};
/**
* Creates a new list binding for the given path and optional context which must
* resolve to an absolute OData path for an entity set.
*
* @param {string} sPath
* The binding path in the model; must not end with a slash
* @param {sap.ui.model.Context} [oContext]
* The context which is required as base for a relative path
* @param {sap.ui.model.Sorter|sap.ui.model.Sorter[]} [vSorters]
* The dynamic sorters to be used initially. Call
* {@link sap.ui.model.odata.v4.ODataListBinding#sort} to replace them. Static sorters, as
* defined in the '$orderby' binding parameter, are always executed after the dynamic sorters.
* Supported since 1.39.0.
* @param {sap.ui.model.Filter|sap.ui.model.Filter[]} [vFilters]
* The dynamic application filters to be used initially. Call
* {@link sap.ui.model.odata.v4.ODataListBinding#filter} to replace them. Static filters, as
* defined in the '$filter' binding parameter, are always combined with the dynamic filters
* using a logical <code>AND</code>.
* Supported since 1.39.0.
* @param {object} [mParameters]
* Map of binding parameters which can be OData query options as specified in <a href=
* "https://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part2-url-conventions.html"
* >"OData Version 4.0 Part 2: URL Conventions"</a> or binding-specific parameters as
* specified below.
* Note: The binding creates its own data service request if it is absolute or if it has any
* parameters or if it is relative and has a context created via {@link #createBindingContext}
* or if it has sorters or filters.
* The following OData query options are allowed:
* <ul>
* <li> All "5.2 Custom Query Options" except for those with a name starting with "sap-"
* (unless starting with "sap-valid-")
* <li> The $apply, $count, $expand, $filter, $levels, $orderby, $search, and $select
* "5.1 System Query Options"; OData V4 only allows $levels inside $expand.
* </ul>
* All other query options lead to an error.
* Query options specified for the binding overwrite model query options.
* @param {string} [mParameters.$apply]
* The value for the "3 System Query Option $apply" (see also
* <a href="https://docs.oasis-open.org/odata/odata-data-aggregation-ext/v4.0/">OData
* Extension for Data Aggregation Version 4.0</a>) as an alternative to
* <code>$$aggregation</code>
* @param {string|boolean} [mParameters.$count]
* The value for the "5.1.6 System Query Option $count", useful for creation at the end and
* {@link sap.ui.model.odata.v4.ODataListBinding#getCount}
* @param {string|object} [mParameters.$expand]
* The value for the "5.1.2 System Query Option $expand" or an object which determines that
* value. The object is a map from expand path to expand options, where the options are again
* maps of system query options, typically with string values. $count can also be given as a
* <code>boolean</code> value, $expand can recursively be given as a map, $levels can also be
* given as a <code>number</code> value, and $select can also be given as an array (but
* without navigation paths). An empty map can also be given as <code>null</code> or
* <code>true</code>. See also {@link topic:1ab4f62de6ab467096a2a98b363a1373 Parameters}.
* @param {string} [mParameters.$filter]
* The value for the "5.1.1 System Query Option $filter" used in addition to
* <code>vFilters</code>
* @param {string|number} [mParameters.$orderby]
* The value for the "5.1.4 System Query Option $orderby" used in addition to
* <code>vSorters</code>
* @param {string} [mParameters.$search]
* The value for the "5.1.7 System Query Option $search"; see also
* <code>oAggregation.search</code> at
* {@link sap.ui.model.odata.v4.ODataListBinding#setAggregation} and the note there!
* @param {string|string[]} [mParameters.$select]
* A comma separated list or an array of items which determine the value for the
* "5.1.3 System Query Option $select". Since 1.75.0, when using the "autoExpandSelect" model
* parameter (see {@link sap.ui.model.odata.v4.ODataModel#constructor}), paths with navigation
* properties can be included and will contribute to the "5.1.2 System Query Option $expand".
* @param {object} [mParameters.$$aggregation]
* An object holding the information needed for data aggregation, see
* {@link sap.ui.model.odata.v4.ODataListBinding#setAggregation} for details.
* @param {boolean} [mParameters.$$canonicalPath]
* Whether a binding relative to an {@link sap.ui.model.odata.v4.Context} uses the canonical
* path computed from its context's path for data service requests; only the value
* <code>true</code> is allowed.
* @param {boolean} [mParameters.$$getKeepAliveContext]
* Whether this binding is considered for a match when {@link #getKeepAliveContext} is called;
* only the value <code>true</code> is allowed. Must not be combined with <code>$apply</code>,
* <code>$$aggregation</code>, <code>$$canonicalPath</code>, or <code>$$sharedRequest</code>.
* If the binding is relative, <code>$$ownRequest</code> must be set as well.
* Supported since 1.99.0
* @param {string} [mParameters.$$groupId]
* The group ID to be used for <b>read</b> requests triggered by this binding; if not
* specified, either the parent binding's group ID (if the binding is relative) or the
* model's group ID is used, see {@link sap.ui.model.odata.v4.ODataModel#constructor}.
* Valid values are <code>undefined</code>, '$auto', '$auto.*', '$direct' or application group
* IDs as specified in {@link sap.ui.model.odata.v4.ODataModel}.
* @param {sap.ui.model.odata.OperationMode} [mParameters.$$operationMode]
* The operation mode for filtering and sorting with the model's operation mode as default.
* Since 1.39.0, the operation mode {@link sap.ui.model.odata.OperationMode.Server} is
* supported. All other operation modes including <code>undefined</code> lead to an error if
* 'vFilters' or 'vSorters' are given or if
* {@link sap.ui.model.odata.v4.ODataListBinding#filter} or
* {@link sap.ui.model.odata.v4.ODataListBinding#sort} is called.
* @param {boolean} [mParameters.$$patchWithoutSideEffects]
* Whether implicit loading of side effects via PATCH requests is switched off; only the value
* <code>true</code> is allowed. This sets the preference "return=minimal" and requires the
* service to return an ETag header for "204 No Content" responses. If not specified, the
* value of the parent binding is used.
* @param {boolean} [mParameters.$$ownRequest]
* Whether the binding always uses an own service request to read its data; only the value
* <code>true</code> is allowed.
* @param {boolean} [mParameters.$$sharedRequest]
* Whether multiple bindings for the same resource path share the data, so that it is
* requested only once; only the value <code>true</code> is allowed. This parameter can be
* inherited from the model's parameter "sharedRequests", see
* {@link sap.ui.model.odata.v4.ODataModel#constructor}. Supported since 1.80.0
* <b>Note:</b> These bindings are read-only, so they may be especially useful for value
* lists; state messages (since 1.108.0) and the following APIs are <b>not</b> allowed
* <ul>
* <li> for the list binding itself:
* <ul>
* <li> {@link sap.ui.model.odata.v4.ODataListBinding#create}
* <li> {@link sap.ui.model.odata.v4.ODataListBinding#getKeepAliveContext} or
* {@link #getKeepAliveContext} as far as it affects such a list binding
* <li> {@link sap.ui.model.odata.v4.ODataListBinding#resetChanges}
* </ul>
* <li> for the {@link sap.ui.model.odata.v4.ODataListBinding#getHeaderContext header
* context} of a list binding:
* <ul>
* <li> {@link sap.ui.model.odata.v4.Context#requestSideEffects}
* </ul>
* <li> for the context of a list binding representing a single entity:
* <ul>
* <li> {@link sap.ui.model.odata.v4.Context#delete}
* <li> {@link sap.ui.model.odata.v4.Context#refresh}
* <li> {@link sap.ui.model.odata.v4.Context#replaceWith}
* <li> {@link sap.ui.model.odata.v4.Context#requestSideEffects}
* <li> {@link sap.ui.model.odata.v4.Context#setKeepAlive}
* <li> {@link sap.ui.model.odata.v4.Context#setProperty}
* <li> executing a bound operation using <code>bReplaceWithRVC</code>, see
* {@link sap.ui.model.odata.v4.ODataContextBinding#execute}
* </ul>
* <li> for a dependent property binding of the list binding:
* <ul>
* <li> {@link sap.ui.model.odata.v4.ODataPropertyBinding#setValue}
* </ul>
* </ul>
* @param {string} [mParameters.$$updateGroupId]
* The group ID to be used for <b>update</b> requests triggered by this binding;
* if not specified, either the parent binding's update group ID (if the binding is relative)
* or the model's update group ID is used,
* see {@link sap.ui.model.odata.v4.ODataModel#constructor}.
* For valid values, see parameter "$$groupId".
* @returns {sap.ui.model.odata.v4.ODataListBinding}
* The list binding
* @throws {Error}
* If disallowed binding parameters are provided or an unsupported operation mode is used
*
* @public
* @see sap.ui.model.Model#bindList
* @since 1.37.0
*/
ODataModel.prototype.bindList = function (sPath, oContext, vSorters, vFilters, mParameters) {
return new ODataListBinding(this, sPath, oContext, vSorters, vFilters, mParameters);
};
/**
* Creates a new property binding for the given path. This binding is inactive and will not
* know the property value initially. You have to call {@link sap.ui.model.Binding#initialize}
* to get it updated asynchronously and register a change listener at the binding to be informed
* when the value is available.
*
* It is possible to create a property binding pointing to metadata. A '##' in the
* binding's path is recognized as a separator and splits it into two parts.
* The part before the separator is resolved with the binding's context and the result is
* transformed into a metadata context (see
* {@link sap.ui.model.odata.v4.ODataMetaModel#getMetaContext}). The part following the
* separator is then interpreted relative to this metadata context, even if it starts with
* a '/'; see {@link sap.ui.model.odata.v4.ODataMetaModel#requestObject} for more details.
*
* If the target type specified in the corresponding control property's binding info is "any"
* and the binding is relative or points to metadata, the binding may have an object value;
* in this case and unless the binding refers to an action advertisement the binding's mode must
* be {@link sap.ui.model.BindingMode.OneTime}.
*
* @param {string} sPath
* The binding path in the model; must not end with a slash
* @param {sap.ui.model.Context} [oContext]
* The context which is required as base for a relative path
* @param {object} [mParameters]
* Map of binding parameters which can be OData query options as specified in <a href=
* "https://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part2-url-conventions.html"
* >"OData Version 4.0 Part 2: URL Conventions"</a> or the binding-specific parameters as
* specified below. The following OData query options are allowed:
* <ul>
* <li> All "5.2 Custom Query Options" except for those with a name starting with "sap-"
* (unless starting with "sap-valid-")
* <li> The $apply, $filter, and $search "5.1 System Query Options" if the path ends with a
* "$count" segment.
* </ul>
* All other query options lead to an error.
* Query options specified for the binding overwrite model query options.
* Note: The binding only creates its own data service request if it is absolute or if it is
* relative to a context created via {@link #createBindingContext}. The binding parameters are
* ignored in case the binding creates no own data service request or in case the binding
* points to metadata.
* @param {string} [mParameters.$apply]
* The value for the "3 System Query Option $apply" (see also
* <a href="https://docs.oasis-open.org/odata/odata-data-aggregation-ext/v4.0/">OData
* Extension for Data Aggregation Version 4.0</a>), if the path ends with a "$count" segment
* @param {string} [mParameters.$filter]
* The value for the "5.